/*
 * FilePond 3.7.6
 * Licensed under MIT, https://opensource.org/licenses/MIT
 * Please visit https://pqina.nl/filepond for details.
 */

/* eslint-disable */
(function(global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined'
      ? factory(exports)
      : typeof define === 'function' && define.amd
        ? define(['exports'], factory)
        : factory((global.FilePond = {}));
  })(this, function(exports) {
    'use strict';

    var isNode = function isNode(value) {
      return value instanceof HTMLElement;
    };

    var createStore = function createStore(initialState) {
      var queries =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
      var actions =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];

      // internal state
      var state = Object.assign({}, initialState);

      // contains all actions for next frame, is clear when actions are requested
      var actionQueue = [];
      var dispatchQueue = [];

      // returns a duplicate of the current state
      var getState = function getState() {
        return Object.assign({}, state);
      };

      // returns a duplicate of the actions array and clears the actions array
      var processActionQueue = function processActionQueue() {
        // create copy of actions queue
        var queue = [].concat(actionQueue);

        // clear actions queue (we don't want no double actions)
        actionQueue.length = 0;

        return queue;
      };

      // processes actions that might block the main UI thread
      var processDispatchQueue = function processDispatchQueue() {
        // create copy of actions queue
        var queue = [].concat(dispatchQueue);

        // clear actions queue (we don't want no double actions)
        dispatchQueue.length = 0;

        // now dispatch these actions
        queue.forEach(function(_ref) {
          var type = _ref.type,
            data = _ref.data;

          dispatch(type, data);
        });
      };

      // adds a new action, calls its handler and
      var dispatch = function dispatch(type, data, isBlocking) {
        // is blocking action
        if (isBlocking) {
          dispatchQueue.push({
            type: type,
            data: data
          });
          return;
        }

        // if this action has a handler, handle the action
        if (actionHandlers[type]) {
          actionHandlers[type](data);
        }

        // now add action
        actionQueue.push({
          type: type,
          data: data
        });
      };

      var query = function query(str) {
        var _queryHandles;

        for (
          var _len = arguments.length,
            args = Array(_len > 1 ? _len - 1 : 0),
            _key = 1;
          _key < _len;
          _key++
        ) {
          args[_key - 1] = arguments[_key];
        }

        return queryHandles[str]
          ? (_queryHandles = queryHandles)[str].apply(_queryHandles, args)
          : null;
      };

      var api = {
        getState: getState,
        processActionQueue: processActionQueue,
        processDispatchQueue: processDispatchQueue,
        dispatch: dispatch,
        query: query
      };

      var queryHandles = {};
      queries.forEach(function(query) {
        queryHandles = Object.assign({}, query(state), queryHandles);
      });

      var actionHandlers = {};
      actions.forEach(function(action) {
        actionHandlers = Object.assign(
          {},
          action(dispatch, query, state),
          actionHandlers
        );
      });

      return api;
    };

    var defineProperty = function defineProperty(obj, property, definition) {
      if (typeof definition === 'function') {
        obj[property] = definition;
        return;
      }
      Object.defineProperty(obj, property, Object.assign({}, definition));
    };

    var forin = function forin(obj, cb) {
      for (var key in obj) {
        if (!obj.hasOwnProperty(key)) {
          continue;
        }

        cb(key, obj[key]);
      }
    };

    var createObject = function createObject(definition) {
      var obj = {};
      forin(definition, function(property) {
        defineProperty(obj, property, definition[property]);
      });
      return obj;
    };

    var attr = function attr(node, name) {
      var value =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

      if (value === null) {
        return node.getAttribute(name) || node.hasAttribute(name);
      }
      node.setAttribute(name, value);
    };

    var _typeof =
      typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
        ? function(obj) {
            return typeof obj;
          }
        : function(obj) {
            return obj &&
              typeof Symbol === 'function' &&
              obj.constructor === Symbol &&
              obj !== Symbol.prototype
              ? 'symbol'
              : typeof obj;
          };

    var slicedToArray = (function() {
      function sliceIterator(arr, i) {
        var _arr = [];
        var _n = true;
        var _d = false;
        var _e = undefined;

        try {
          for (
            var _i = arr[Symbol.iterator](), _s;
            !(_n = (_s = _i.next()).done);
            _n = true
          ) {
            _arr.push(_s.value);

            if (i && _arr.length === i) break;
          }
        } catch (err) {
          _d = true;
          _e = err;
        } finally {
          try {
            if (!_n && _i['return']) _i['return']();
          } finally {
            if (_d) throw _e;
          }
        }

        return _arr;
      }

      return function(arr, i) {
        if (Array.isArray(arr)) {
          return arr;
        } else if (Symbol.iterator in Object(arr)) {
          return sliceIterator(arr, i);
        } else {
          throw new TypeError(
            'Invalid attempt to destructure non-iterable instance'
          );
        }
      };
    })();

    var toConsumableArray = function(arr) {
      if (Array.isArray(arr)) {
        for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++)
          arr2[i] = arr[i];

        return arr2;
      } else {
        return Array.from(arr);
      }
    };

    var ns = 'http://www.w3.org/2000/svg';
    var svgElements = ['svg', 'path']; // only svg elements used

    var isSVGElement = function isSVGElement(tag) {
      return svgElements.includes(tag);
    };

    var createElement = function createElement(tag, className) {
      var attributes =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (
        (typeof className === 'undefined' ? 'undefined' : _typeof(className)) ===
        'object'
      ) {
        attributes = className;
        className = null;
      }
      var element = isSVGElement(tag)
        ? document.createElementNS(ns, tag)
        : document.createElement(tag);
      if (className) {
        if (isSVGElement(tag)) {
          attr(element, 'class', className);
        } else {
          element.className = className;
        }
      }
      forin(attributes, function(name, value) {
        attr(element, name, value);
      });
      return element;
    };

    var appendChild = function appendChild(parent) {
      return function(child, index) {
        if (typeof index !== 'undefined' && parent.children[index]) {
          parent.insertBefore(child, parent.children[index]);
        } else {
          parent.appendChild(child);
        }
      };
    };

    var appendChildView = function appendChildView(parent, childViews) {
      return function(view, index) {
        if (typeof index !== 'undefined') {
          childViews.splice(index, 0, view);
        } else {
          childViews.push(view);
        }

        return view;
      };
    };

    var removeChildView = function removeChildView(parent, childViews) {
      return function(view) {
        // remove from child views
        childViews.splice(childViews.indexOf(view), 1);

        // remove the element
        if (view.element.parentNode) {
          parent.removeChild(view.element);
        }

        return view;
      };
    };

    var getViewRect = function getViewRect(
      elementRect,
      childViews,
      offset,
      scale
    ) {
      var left = offset[0] || elementRect.left;
      var top = offset[1] || elementRect.top;
      var right = left + elementRect.width;
      var bottom = top + elementRect.height * (scale[1] || 1);

      var rect = {
        // the rectangle of the element itself
        element: Object.assign({}, elementRect),

        // the rectangle of the element expanded to contain its children, does not include any margins
        inner: {
          left: elementRect.left,
          top: elementRect.top,
          right: elementRect.right,
          bottom: elementRect.bottom
        },

        // the rectangle of the element expanded to contain its children including own margin and child margins
        // margins will be added after we've recalculated the size
        outer: {
          left: left,
          top: top,
          right: right,
          bottom: bottom
        }
      };

      // expand rect to fit all child rectangles
      childViews
        .filter(function(childView) {
          return !childView.isRectIgnored();
        })
        .map(function(childView) {
          return childView.rect;
        })
        .forEach(function(childViewRect) {
          expandRect(rect.inner, Object.assign({}, childViewRect.inner));
          expandRect(rect.outer, Object.assign({}, childViewRect.outer));
        });

      // calculate inner width and height
      calculateRectSize(rect.inner);

      // append additional margin (top and left margins are included in top and left automatically)
      rect.outer.bottom += rect.element.marginBottom;
      rect.outer.right += rect.element.marginRight;

      // calculate outer width and height
      calculateRectSize(rect.outer);

      return rect;
    };

    var expandRect = function expandRect(parent, child) {
      // adjust for parent offset
      child.top += parent.top;
      child.right += parent.left;
      child.bottom += parent.top;
      child.left += parent.left;

      if (child.bottom > parent.bottom) {
        parent.bottom = child.bottom;
      }

      if (child.right > parent.right) {
        parent.right = child.right;
      }
    };

    var calculateRectSize = function calculateRectSize(rect) {
      rect.width = rect.right - rect.left;
      rect.height = rect.bottom - rect.top;
    };

    var isNumber = function isNumber(value) {
      return typeof value === 'number';
    };

    /**
     * Determines if position is at destination
     * @param position
     * @param destination
     * @param velocity
     * @param errorMargin
     * @returns {boolean}
     */
    var thereYet = function thereYet(position, destination, velocity) {
      var errorMargin =
        arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.001;

      return (
        Math.abs(position - destination) < errorMargin &&
        Math.abs(velocity) < errorMargin
      );
    };

    /**
     * Spring animation
     */
    var spring =
      // default options
      function spring() // method definition
      {
        var _ref =
            arguments.length > 0 && arguments[0] !== undefined
              ? arguments[0]
              : {},
          _ref$stiffness = _ref.stiffness,
          stiffness = _ref$stiffness === undefined ? 0.5 : _ref$stiffness,
          _ref$damping = _ref.damping,
          damping = _ref$damping === undefined ? 0.75 : _ref$damping,
          _ref$mass = _ref.mass,
          mass = _ref$mass === undefined ? 10 : _ref$mass;

        var target = null;
        var position = null;
        var velocity = 0;
        var resting = false;

        // updates spring state
        var interpolate = function interpolate() {
          // in rest, don't animate
          if (resting) {
            return;
          }

          // need at least a target or position to do springy things
          if (!(isNumber(target) && isNumber(position))) {
            resting = true;
            velocity = 0;
            return;
          }

          // calculate spring force
          var f = -(position - target) * stiffness;

          // update velocity by adding force based on mass
          velocity += f / mass;

          // update position by adding velocity
          position += velocity;

          // slow down based on amount of damping
          velocity *= damping;

          // we've arrived if we're near target and our velocity is near zero
          if (thereYet(position, target, velocity)) {
            position = target;
            velocity = 0;
            resting = true;

            // we done
            api.onupdate(position);
            api.oncomplete(position);
          } else {
            // progress update
            api.onupdate(position);
          }
        };

        /**
         * Set new target value
         * @param value
         */
        var setTarget = function setTarget(value) {
          // if currently has no position, set target and position to this value
          if (isNumber(value) && !isNumber(position)) {
            position = value;
          }

          // next target value will not be animated to
          if (target === null) {
            target = value;
            position = value;
          }

          // let start moving to target
          target = value;

          // already at target
          if (position === target || typeof target === 'undefined') {
            // now resting as target is current position, stop moving
            resting = true;
            velocity = 0;

            // done!
            api.onupdate(position);
            api.oncomplete(position);

            return;
          }

          resting = false;
        };

        // need 'api' to call onupdate callback
        var api = createObject({
          interpolate: interpolate,
          target: {
            set: setTarget,
            get: function get() {
              return target;
            }
          },
          resting: {
            get: function get() {
              return resting;
            }
          },
          onupdate: function onupdate(value) {},
          oncomplete: function oncomplete(value) {}
        });

        return api;
      };

    var easeInOutQuad = function easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    };

    var tween =
      // default values
      function tween() // method definition
      {
        var _ref =
            arguments.length > 0 && arguments[0] !== undefined
              ? arguments[0]
              : {},
          _ref$duration = _ref.duration,
          duration = _ref$duration === undefined ? 500 : _ref$duration,
          _ref$easing = _ref.easing,
          easing = _ref$easing === undefined ? easeInOutQuad : _ref$easing,
          _ref$delay = _ref.delay,
          delay = _ref$delay === undefined ? 0 : _ref$delay;

        var start = null;
        var t = void 0;
        var p = void 0;
        var resting = true;
        var reverse = false;
        var target = null;

        var interpolate = function interpolate(ts) {
          if (resting || target === null) {
            return;
          }

          if (start === null) {
            start = ts;
          }

          if (ts - start < delay) {
            return;
          }

          t = ts - start - delay;

          if (t < duration) {
            p = t / duration;
            api.onupdate((t >= 0 ? easing(reverse ? 1 - p : p) : 0) * target);
          } else {
            t = 1;
            p = reverse ? 0 : 1;
            api.onupdate(p * target);
            api.oncomplete(p * target);
            resting = true;
          }
        };

        // need 'api' to call onupdate callback
        var api = createObject({
          interpolate: interpolate,
          target: {
            get: function get() {
              return reverse ? 0 : target;
            },
            set: function set(value) {
              // is initial value
              if (target === null) {
                target = value;
                api.onupdate(value);
                api.oncomplete(value);
                return;
              }

              // want to tween to a smaller value and have a current value
              if (value < target) {
                target = 1;
                reverse = true;
              } else {
                // not tweening to a smaller value
                reverse = false;
                target = value;
              }

              // let's go!
              resting = false;
              start = null;
            }
          },
          resting: {
            get: function get() {
              return resting;
            }
          },
          onupdate: function onupdate(value) {},
          oncomplete: function oncomplete(value) {}
        });

        return api;
      };

    var animator = {
      spring: spring,
      tween: tween
    };

    /*
   { type: 'spring', stiffness: .5, damping: .75, mass: 10 };
   { translation: { type: 'spring', ... }, ... }
   { translation: { x: { type: 'spring', ... } } }
  */
    var createAnimator = function createAnimator(definition, category, property) {
      // default is single definition
      // we check if transform is set, if so, we check if property is set
      var def =
        definition[category] &&
        _typeof(definition[category][property]) === 'object'
          ? definition[category][property]
          : definition[category] || definition;

      var type = typeof def === 'string' ? def : def.type;
      var props =
        (typeof def === 'undefined' ? 'undefined' : _typeof(def)) === 'object'
          ? Object.assign({}, def)
          : {};

      return animator[type] ? animator[type](props) : null;
    };

    var addGetSet = function addGetSet(keys, obj, props) {
      var overwrite =
        arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

      obj = Array.isArray(obj) ? obj : [obj];
      obj.forEach(function(o) {
        keys.forEach(function(key) {
          var name = key;
          var getter = function getter() {
            return props[key];
          };
          var setter = function setter(value) {
            return (props[key] = value);
          };

          if (
            (typeof key === 'undefined' ? 'undefined' : _typeof(key)) === 'object'
          ) {
            name = key.key;
            getter = key.getter || getter;
            setter = key.setter || setter;
          }

          if (o[name] && !overwrite) {
            return;
          }

          o[name] = {
            get: getter,
            set: setter
          };
        });
      });
    };

    var isDefined = function isDefined(value) {
      return value != null;
    };

    // add to state,
    // add getters and setters to internal and external api (if not set)
    // setup animators

    var animations = function animations(_ref) {
      var mixinConfig = _ref.mixinConfig,
        viewProps = _ref.viewProps,
        viewInternalAPI = _ref.viewInternalAPI,
        viewExternalAPI = _ref.viewExternalAPI;

      // initial properties
      var initialProps = Object.assign({}, viewProps);

      // list of all active animations
      var animations = [];

      // setup animators
      forin(mixinConfig, function(property, animation) {
        var animator = createAnimator(animation);
        if (!animator) {
          return;
        }

        // when the animator updates, update the view state value
        animator.onupdate = function(value) {
          viewProps[property] = value;
        };

        // set animator target
        animator.target = initialProps[property];

        // when value is set, set the animator target value
        var prop = {
          key: property,
          setter: function setter(value) {
            // if already at target, we done!
            if (animator.target === value) {
              return;
            }

            animator.target = value;
          },
          getter: function getter() {
            return viewProps[property];
          }
        };

        // add getters and setters
        addGetSet([prop], [viewInternalAPI, viewExternalAPI], viewProps, true);

        // add it to the list for easy updating from the _write method
        animations.push(animator);
      });

      // expose internal write api
      return {
        write: function write(ts) {
          var resting = true;
          animations.forEach(function(animation) {
            if (!animation.resting) {
              resting = false;
            }
            animation.interpolate(ts);
          });
          return resting;
        },
        destroy: function destroy() {}
      };
    };

    var addEvent = function addEvent(element) {
      return function(type, fn) {
        element.addEventListener(type, fn);
      };
    };

    var removeEvent = function removeEvent(element) {
      return function(type, fn) {
        element.removeEventListener(type, fn);
      };
    };

    // mixin
    var listeners = function listeners(_ref) {
      var mixinConfig = _ref.mixinConfig,
        viewProps = _ref.viewProps,
        viewInternalAPI = _ref.viewInternalAPI,
        viewExternalAPI = _ref.viewExternalAPI,
        viewState = _ref.viewState,
        view = _ref.view;

      var events = [];

      var add = addEvent(view.element);
      var remove = removeEvent(view.element);

      viewExternalAPI.on = function(type, fn) {
        events.push({
          type: type,
          fn: fn
        });
        add(type, fn);
      };

      viewExternalAPI.off = function(type, fn) {
        events.splice(
          events.findIndex(function(event) {
            return event.type === type && event.fn === fn;
          }),
          1
        );
        remove(type, fn);
      };

      return {
        write: function write() {
          // not busy
          return true;
        },
        destroy: function destroy() {
          events.forEach(function(event) {
            remove(event.type, event.fn);
          });
        }
      };
    };

    // add to external api and link to props

    var apis = function apis(_ref) {
      var mixinConfig = _ref.mixinConfig,
        viewProps = _ref.viewProps,
        viewExternalAPI = _ref.viewExternalAPI;

      addGetSet(mixinConfig, viewExternalAPI, viewProps);
    };

    // add to state,
    // add getters and setters to internal and external api (if not set)
    // set initial state based on props in viewProps
    // apply as transforms each frame

    var defaults$1 = {
      opacity: 1,
      scaleX: 1,
      scaleY: 1,
      translateX: 0,
      translateY: 0,
      rotateX: 0,
      rotateY: 0,
      rotateZ: 0,
      originX: 0,
      originY: 0
    };

    var styles = function styles(_ref) {
      var mixinConfig = _ref.mixinConfig,
        viewProps = _ref.viewProps,
        viewInternalAPI = _ref.viewInternalAPI,
        viewExternalAPI = _ref.viewExternalAPI,
        view = _ref.view;

      // initial props
      var initialProps = Object.assign({}, viewProps);

      // current props
      var currentProps = {};

      // we will add those properties to the external API and link them to the viewState
      addGetSet(mixinConfig, [viewInternalAPI, viewExternalAPI], viewProps);

      // override rect on internal and external rect getter so it takes in account transforms
      var getOffset = function getOffset() {
        return [viewProps['translateX'] || 0, viewProps['translateY'] || 0];
      };
      var getScale = function getScale() {
        return [viewProps['scaleX'] || 0, viewProps['scaleY'] || 0];
      };
      var getRect = function getRect() {
        return view.rect
          ? getViewRect(view.rect, view.childViews, getOffset(), getScale())
          : null;
      };
      viewInternalAPI.rect = { get: getRect };
      viewExternalAPI.rect = { get: getRect };

      // apply view props
      mixinConfig.forEach(function(key) {
        viewProps[key] =
          typeof initialProps[key] === 'undefined'
            ? defaults$1[key]
            : initialProps[key];
      });

      // expose api
      return {
        write: function write() {
          // see if props have changed
          if (!propsHaveChanged(currentProps, viewProps)) {
            return;
          }

          // moves element to correct position on screen
          applyStyles(view.element, viewProps);

          // store new transforms
          Object.assign(currentProps, Object.assign({}, viewProps));

          // no longer busy
          return true;
        },
        destroy: function destroy() {}
      };
    };

    var propsHaveChanged = function propsHaveChanged(currentProps, newProps) {
      // different amount of keys
      if (Object.keys(currentProps).length !== Object.keys(newProps).length) {
        return true;
      }

      // lets analyze the individual props
      for (var prop in newProps) {
        if (newProps[prop] !== currentProps[prop]) {
          return true;
        }
      }

      return false;
    };

    var applyStyles = function applyStyles(element, _ref2) {
      var opacity = _ref2.opacity,
        perspective = _ref2.perspective,
        translateX = _ref2.translateX,
        translateY = _ref2.translateY,
        scaleX = _ref2.scaleX,
        scaleY = _ref2.scaleY,
        rotateX = _ref2.rotateX,
        rotateY = _ref2.rotateY,
        rotateZ = _ref2.rotateZ,
        originX = _ref2.originX,
        originY = _ref2.originY,
        width = _ref2.width,
        height = _ref2.height;

      var transforms = '';
      var styles = '';

      // handle transform origin
      if (isDefined(originX) || isDefined(originY)) {
        styles +=
          'transform-origin: ' + (originX || 0) + 'px ' + (originY || 0) + 'px;';
      }

      // transform order is relevant
      // 0. perspective
      if (isDefined(perspective)) {
        transforms += 'perspective(' + perspective + 'px) ';
      }

      // 1. translate
      if (isDefined(translateX) || isDefined(translateY)) {
        transforms +=
          'translate3d(' +
          (translateX || 0) +
          'px, ' +
          (translateY || 0) +
          'px, 0) ';
      }

      // 2. scale
      if (isDefined(scaleX) || isDefined(scaleY)) {
        transforms +=
          'scale3d(' +
          (isDefined(scaleX) ? scaleX : 1) +
          ', ' +
          (isDefined(scaleY) ? scaleY : 1) +
          ', 1) ';
      }

      // 3. rotate
      if (isDefined(rotateZ)) {
        transforms += 'rotateZ(' + rotateZ + 'rad) ';
      }

      if (isDefined(rotateX)) {
        transforms += 'rotateX(' + rotateX + 'rad) ';
      }

      if (isDefined(rotateY)) {
        transforms += 'rotateY(' + rotateY + 'rad) ';
      }

      // add transforms
      if (transforms.length) {
        styles += 'transform:' + transforms + ';';
      }

      // add opacity
      if (isDefined(opacity)) {
        styles += 'opacity:' + opacity + ';';

        // if we reach zero, we make the element inaccessible
        if (opacity === 0) {
          styles += 'visibility:hidden;';
        }

        // if we're below 100% opacity this element can't be clicked
        if (opacity < 1) {
          styles += 'pointer-events:none;';
        }
      }

      // add height
      if (isDefined(height)) {
        styles += 'height:' + height + 'px;';
      }

      // add width
      if (isDefined(width)) {
        styles += 'width:' + width + 'px;';
      }

      // apply styles
      var elementCurrentStyle = element.elementCurrentStyle || '';

      // if new styles does not match current styles, lets update!
      if (
        styles.length !== elementCurrentStyle.length ||
        styles !== elementCurrentStyle
      ) {
        element.setAttribute('style', styles);
        // store current styles so we can compare them to new styles later on
        // _not_ getting the style attribute is faster
        element.elementCurrentStyle = styles;
      }
    };

    var Mixins = {
      styles: styles,
      listeners: listeners,
      animations: animations,
      apis: apis
    };

    var updateRect = function updateRect() {
      var rect =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var element =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var style =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!element.layoutCalculated) {
        rect.paddingTop = parseInt(style.paddingTop, 10) || 0;
        rect.marginTop = parseInt(style.marginTop, 10) || 0;
        rect.marginRight = parseInt(style.marginRight, 10) || 0;
        rect.marginBottom = parseInt(style.marginBottom, 10) || 0;
        rect.marginLeft = parseInt(style.marginLeft, 10) || 0;
        element.layoutCalculated = true;
      }

      rect.left = element.offsetLeft || 0;
      rect.top = element.offsetTop || 0;
      rect.width = element.offsetWidth || 0;
      rect.height = element.offsetHeight || 0;

      rect.right = rect.left + rect.width;
      rect.bottom = rect.top + rect.height;

      rect.scrollTop = element.scrollTop;

      rect.hidden = element.offsetParent === null;

      return rect;
    };

    var createView =
      // default view definition
      function createView() {
        var _ref =
            arguments.length > 0 && arguments[0] !== undefined
              ? arguments[0]
              : {},
          _ref$tag = _ref.tag,
          tag = _ref$tag === undefined ? 'div' : _ref$tag,
          _ref$name = _ref.name,
          name = _ref$name === undefined ? null : _ref$name,
          _ref$attributes = _ref.attributes,
          attributes = _ref$attributes === undefined ? {} : _ref$attributes,
          _ref$read = _ref.read,
          read = _ref$read === undefined ? function() {} : _ref$read,
          _ref$write = _ref.write,
          write = _ref$write === undefined ? function() {} : _ref$write,
          _ref$create = _ref.create,
          create = _ref$create === undefined ? function() {} : _ref$create,
          _ref$destroy = _ref.destroy,
          destroy = _ref$destroy === undefined ? function() {} : _ref$destroy,
          _ref$filterFrameActio = _ref.filterFrameActionsForChild,
          filterFrameActionsForChild =
            _ref$filterFrameActio === undefined
              ? function(child, actions) {
                  return actions;
                }
              : _ref$filterFrameActio,
          _ref$didCreateView = _ref.didCreateView,
          didCreateView =
            _ref$didCreateView === undefined ? function() {} : _ref$didCreateView,
          _ref$didWriteView = _ref.didWriteView,
          didWriteView =
            _ref$didWriteView === undefined ? function() {} : _ref$didWriteView,
          _ref$ignoreRect = _ref.ignoreRect,
          ignoreRect = _ref$ignoreRect === undefined ? false : _ref$ignoreRect,
          _ref$ignoreRectUpdate = _ref.ignoreRectUpdate,
          ignoreRectUpdate =
            _ref$ignoreRectUpdate === undefined ? false : _ref$ignoreRectUpdate,
          _ref$mixins = _ref.mixins,
          mixins = _ref$mixins === undefined ? [] : _ref$mixins;

        return function(
          // each view requires reference to store
          store
        ) {
          var props =
            arguments.length > 1 && arguments[1] !== undefined
              ? arguments[1]
              : {};

          // root element should not be changed
          var element = createElement(tag, 'filepond--' + name, attributes);

          // style reference should also not be changed
          var style = window.getComputedStyle(element, null);

          // element rectangle
          var rect = updateRect();
          var frameRect = null;

          // rest state
          var isResting = false;

          // pretty self explanatory
          var childViews = [];

          // loaded mixins
          var activeMixins = [];

          // references to created children
          var ref = {};

          // state used for each instance
          var state = {};

          // list of writers that will be called to update this view
          var writers = [
            write // default writer
          ];

          var readers = [
            read // default reader
          ];

          var destroyers = [
            destroy // default destroy
          ];

          // core view methods
          var getElement = function getElement() {
            return element;
          };
          var getChildViews = function getChildViews() {
            return [].concat(childViews);
          };
          var getReference = function getReference() {
            return ref;
          };
          var createChildView = function createChildView(store) {
            return function(view, props) {
              return view(store, props);
            };
          };
          var getRect = function getRect() {
            if (frameRect) {
              return frameRect;
            }
            frameRect = getViewRect(rect, childViews, [0, 0], [1, 1]);
            return frameRect;
          };
          var getStyle = function getStyle() {
            return style;
          };

          /**
           * Read data from DOM
           * @private
           */
          var _read = function _read() {
            frameRect = null;

            // read child views
            childViews.forEach(function(child) {
              return child._read();
            });

            var shouldUpdate = !(ignoreRectUpdate && rect.width && rect.height);
            if (shouldUpdate) {
              updateRect(rect, element, style);
            }

            // readers
            var api = { root: internalAPI, props: props, rect: rect };
            readers.forEach(function(reader) {
              return reader(api);
            });
          };

          /**
           * Write data to DOM
           * @private
           */
          var _write = function _write(ts) {
            var frameActions =
              arguments.length > 1 && arguments[1] !== undefined
                ? arguments[1]
                : [];

            // if no actions, we assume that the view is resting
            var resting = frameActions.length === 0;

            // writers
            writers.forEach(function(writer) {
              var writerResting = writer({
                props: props,
                root: internalAPI,
                actions: frameActions,
                timestamp: ts
              });
              if (writerResting === false) {
                resting = false;
              }
            });

            // run mixins
            activeMixins.forEach(function(mixin) {
              // if one of the mixins is still busy after write operation, we are not resting
              var mixinResting = mixin.write(ts);
              if (mixinResting === false) {
                resting = false;
              }
            });

            // updates child views that are currently attached to the DOM
            childViews
              .filter(function(child) {
                return !!child.element.parentNode;
              })
              .forEach(function(child) {
                // if a child view is not resting, we are not resting
                var childResting = child._write(
                  ts,
                  filterFrameActionsForChild(child, frameActions)
                );
                if (!childResting) {
                  resting = false;
                }
              });

            // append new elements to DOM and update those
            childViews
              //.filter(child => !child.element.parentNode)
              .forEach(function(child, index) {
                // skip
                if (child.element.parentNode) {
                  return;
                }

                // append to DOM
                internalAPI.appendChild(child.element, index);

                // call read (need to know the size of these elements)
                child._read();

                // re-call write
                child._write(ts, filterFrameActionsForChild(child, frameActions));

                // we just added somthing to the dom, no rest
                resting = false;
              });

            // update resting state
            isResting = resting;

            didWriteView({
              props: props,
              root: internalAPI,
              actions: frameActions,
              timestamp: ts
            });

            // let parent know if we are resting
            return resting;
          };

          var _destroy = function _destroy() {
            activeMixins.forEach(function(mixin) {
              return mixin.destroy();
            });
            destroyers.forEach(function(destroyer) {
              destroyer({ root: internalAPI, props: props });
            });
            childViews.forEach(function(child) {
              return child._destroy();
            });
          };

          // sharedAPI
          var sharedAPIDefinition = {
            element: {
              get: getElement
            },
            style: {
              get: getStyle
            },
            childViews: {
              get: getChildViews
            }
          };

          // private API definition
          var internalAPIDefinition = Object.assign({}, sharedAPIDefinition, {
            rect: {
              get: getRect
            },

            // access to custom children references
            ref: {
              get: getReference
            },

            // dom modifiers
            is: function is(needle) {
              return name === needle;
            },
            appendChild: appendChild(element),
            createChildView: createChildView(store),
            linkView: function linkView(view) {
              childViews.push(view);
              return view;
            },
            unlinkView: function unlinkView(view) {
              childViews.splice(childViews.indexOf(view), 1);
            },
            appendChildView: appendChildView(element, childViews),
            removeChildView: removeChildView(element, childViews),
            registerWriter: function registerWriter(writer) {
              return writers.push(writer);
            },
            registerReader: function registerReader(reader) {
              return readers.push(reader);
            },
            registerDestroyer: function registerDestroyer(destroyer) {
              return destroyers.push(destroyer);
            },
            invalidateLayout: function invalidateLayout() {
              return (element.layoutCalculated = false);
            },

            // access to data store
            dispatch: store.dispatch,
            query: store.query
          });

          // public view API methods
          var externalAPIDefinition = {
            element: {
              get: getElement
            },
            childViews: {
              get: getChildViews
            },
            rect: {
              get: getRect
            },
            resting: {
              get: function get() {
                return isResting;
              }
            },
            isRectIgnored: function isRectIgnored() {
              return ignoreRect;
            },
            _read: _read,
            _write: _write,
            _destroy: _destroy
          };

          // mixin API methods
          var mixinAPIDefinition = Object.assign({}, sharedAPIDefinition, {
            rect: {
              get: function get() {
                return rect;
              }
            }
          });

          // add mixin functionality
          Object.keys(mixins)
            .sort(function(a, b) {
              // move styles to the back of the mixin list (so adjustments of other mixins are applied to the props correctly)
              if (a === 'styles') {
                return 1;
              } else if (b === 'styles') {
                return -1;
              }
              return 0;
            })
            .forEach(function(key) {
              var mixinAPI = Mixins[key]({
                mixinConfig: mixins[key],
                viewProps: props,
                viewState: state,
                viewInternalAPI: internalAPIDefinition,
                viewExternalAPI: externalAPIDefinition,
                view: createObject(mixinAPIDefinition)
              });

              if (mixinAPI) {
                activeMixins.push(mixinAPI);
              }
            });

          // construct private api
          var internalAPI = createObject(internalAPIDefinition);

          // create the view
          create({
            root: internalAPI,
            props: props
          });

          // append created child views to root node
          var childCount = element.children.length; // need to know the current child count so appending happens in correct order
          childViews.forEach(function(child, index) {
            internalAPI.appendChild(child.element, childCount + index);
          });

          // call did create
          didCreateView(internalAPI);

          // expose public api
          return createObject(externalAPIDefinition, props);
        };
      };

    var createPainter = function createPainter(read, write) {
      var fps =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 60;

      var name = '__framePainter';

      // set global painter
      if (window[name]) {
        window[name].readers.push(read);
        window[name].writers.push(write);
        return;
      }

      window[name] = {
        readers: [read],
        writers: [write]
      };

      var painter = window[name];

      var interval = 1000 / fps;
      var last = null;
      var frame = null;

      var tick = function tick(ts) {
        // queue next tick
        frame = window.requestAnimationFrame(tick);

        // limit fps
        if (!last) {
          last = ts;
        }

        var delta = ts - last;

        if (delta <= interval) {
          // skip frame
          return;
        }

        // align next frame
        last = ts - delta % interval;

        // update view
        painter.readers.forEach(function(read) {
          return read();
        });
        painter.writers.forEach(function(write) {
          return write(ts);
        });
      };

      tick(performance.now());

      return {
        pause: function pause() {
          window.cancelAnimationFrame(frame);
        }
      };
    };

    var createRoute = function createRoute(routes, fn) {
      return function(_ref) {
        var root = _ref.root,
          props = _ref.props,
          _ref$actions = _ref.actions,
          actions = _ref$actions === undefined ? [] : _ref$actions,
          timestamp = _ref.timestamp;

        actions
          .filter(function(action) {
            return routes[action.type];
          })
          .forEach(function(action) {
            return routes[action.type]({
              root: root,
              props: props,
              action: action.data,
              timestamp: timestamp
            });
          });
        if (fn) {
          fn({
            root: root,
            props: props,
            actions: actions,
            timestamp: timestamp
          });
        }
      };
    };

    var insertBefore = function insertBefore(newNode, referenceNode) {
      return referenceNode.parentNode.insertBefore(newNode, referenceNode);
    };

    var insertAfter = function insertAfter(newNode, referenceNode) {
      return referenceNode.parentNode.insertBefore(
        newNode,
        referenceNode.nextSibling
      );
    };

    var isArray = function isArray(value) {
      return Array.isArray(value);
    };

    var isEmpty = function isEmpty(value) {
      return value == null;
    };

    var trim = function trim(str) {
      return str.trim();
    };

    var toString = function toString(value) {
      return '' + value;
    };

    var toArray$1 = function toArray(value) {
      var splitter =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ',';

      if (isEmpty(value)) {
        return [];
      }
      if (isArray(value)) {
        return value;
      }
      return toString(value)
        .split(splitter)
        .map(trim)
        .filter(function(str) {
          return str.length;
        });
    };

    var isBoolean = function isBoolean(value) {
      return typeof value === 'boolean';
    };

    var toBoolean = function toBoolean(value) {
      return isBoolean(value) ? value : value === 'true';
    };

    var isString = function isString(value) {
      return typeof value === 'string';
    };

    var toNumber = function toNumber(value) {
      return isNumber(value)
        ? value
        : isString(value) ? toString(value).replace(/[a-z]+/gi, '') : 0;
    };

    var toInt = function toInt(value) {
      return parseInt(toNumber(value), 10);
    };

    var toFloat = function toFloat(value) {
      return parseFloat(toNumber(value));
    };

    var isInt = function isInt(value) {
      return isNumber(value) && isFinite(value) && Math.floor(value) === value;
    };

    var toBytes = function toBytes(value) {
      // is in bytes
      if (isInt(value)) {
        return value;
      }

      // is natural file size
      var naturalFileSize = toString(value).trim();

      // if is value in megabytes
      if (/MB$/i.test(naturalFileSize)) {
        naturalFileSize = naturalFileSize.replace(/MB$i/, '').trim();
        return toInt(naturalFileSize) * 1000 * 1000;
      }

      // if is value in kilobytes
      if (/KB/i.test(naturalFileSize)) {
        naturalFileSize = naturalFileSize.replace(/KB$i/, '').trim();
        return toInt(naturalFileSize) * 1000;
      }

      return toInt(naturalFileSize);
    };

    var isFunction = function isFunction(value) {
      return typeof value === 'function';
    };

    var toFunctionReference = function toFunctionReference(string) {
      var ref = self;
      var levels = string.split('.');
      var level = null;
      while ((level = levels.shift())) {
        ref = ref[level];
        if (!ref) {
          return null;
        }
      }
      return ref;
    };

    var methods = {
      process: 'POST',
      revert: 'DELETE',
      fetch: 'GET',
      restore: 'GET',
      load: 'GET'
    };

    var createServerAPI = function createServerAPI(outline) {
      var api = {};

      api.url = isString(outline) ? outline : outline.url || '';
      api.timeout = outline.timeout ? parseInt(outline.timeout, 10) : 0;

      forin(methods, function(key) {
        api[key] = createAction(key, outline[key], methods[key], api.timeout);
      });

      // special treatment for remove
      api.remove = outline.remove || null;

      return api;
    };

    var createAction = function createAction(name, outline, method, timeout) {
      // is explicitely set to null so disable
      if (outline === null) {
        return null;
      }

      // if is custom function, done! Dev handles everything.
      if (typeof outline === 'function') {
        return outline;
      }

      // build action object
      var action = {
        url: method === 'GET' ? '?' + name + '=' : '',
        method: method,
        headers: {},
        withCredentials: false,
        timeout: timeout,
        onload: null,
        ondata: null,
        onerror: null
      };

      // is a single url
      if (isString(outline)) {
        action.url = outline;
        return action;
      }

      // overwrite
      Object.assign(action, outline);

      // see if should reformat headers;
      if (isString(action.headers)) {
        var parts = action.headers.split(/:(.+)/);
        action.headers = {
          header: parts[0],
          value: parts[1]
        };
      }

      // if is bool withCredentials
      action.withCredentials = toBoolean(action.withCredentials);

      return action;
    };

    var toServerAPI = function toServerAPI(value) {
      return createServerAPI(value);
    };

    var isNull = function isNull(value) {
      return value === null;
    };

    var isObject = function isObject(value) {
      return (
        (typeof value === 'undefined' ? 'undefined' : _typeof(value)) ===
          'object' && value !== null
      );
    };

    var isAPI = function isAPI(value) {
      return (
        isObject(value) &&
        isString(value.url) &&
        isObject(value.process) &&
        isObject(value.revert) &&
        isObject(value.restore) &&
        isObject(value.fetch)
      );
    };

    var getType = function getType(value) {
      if (isArray(value)) {
        return 'array';
      }

      if (isNull(value)) {
        return 'null';
      }

      if (isInt(value)) {
        return 'int';
      }

      if (/^[0-9]+ ?(?:GB|MB|KB)$/gi.test(value)) {
        return 'bytes';
      }

      if (isAPI(value)) {
        return 'api';
      }

      return typeof value === 'undefined' ? 'undefined' : _typeof(value);
    };

    var replaceSingleQuotes = function replaceSingleQuotes(str) {
      return str
        .replace(/{\s*'/g, '{"')
        .replace(/'\s*}/g, '"}')
        .replace(/'\s*:/g, '":')
        .replace(/:\s*'/g, ':"')
        .replace(/,\s*'/g, ',"')
        .replace(/'\s*,/g, '",');
    };

    var conversionTable = {
      array: toArray$1,
      boolean: toBoolean,
      int: function int(value) {
        return getType(value) === 'bytes' ? toBytes(value) : toInt(value);
      },
      float: toFloat,
      bytes: toBytes,
      string: function string(value) {
        return isFunction(value) ? value : toString(value);
      },
      serverapi: toServerAPI,
      object: function object(value) {
        try {
          return JSON.parse(replaceSingleQuotes(value));
        } catch (e) {
          return null;
        }
      },
      function: function _function(value) {
        return toFunctionReference(value);
      }
    };

    var convertTo = function convertTo(value, type) {
      return conversionTable[type](value);
    };

    var getValueByType = function getValueByType(
      newValue,
      defaultValue,
      valueType
    ) {
      // can always assign default value
      if (newValue === defaultValue) {
        return newValue;
      }

      // get the type of the new value
      var newValueType = getType(newValue);

      // is valid type?
      if (newValueType !== valueType) {
        // is string input, let's attempt to convert
        var convertedValue = convertTo(newValue, valueType);

        // what is the type now
        newValueType = getType(convertedValue);

        // no valid conversions found
        if (convertedValue === null) {
          throw 'Trying to assign value with incorrect type to "' +
            option +
            '", allowed type: "' +
            valueType +
            '"';
        } else {
          newValue = convertedValue;
        }
      }

      // assign new value
      return newValue;
    };

    var createOption = function createOption(defaultValue, valueType) {
      var currentValue = defaultValue;
      return {
        enumerable: true,
        get: function get() {
          return currentValue;
        },
        set: function set(newValue) {
          currentValue = getValueByType(newValue, defaultValue, valueType);
        }
      };
    };

    var createOptions = function createOptions(options) {
      var obj = {};
      forin(options, function(prop) {
        var optionDefinition = options[prop];
        obj[prop] = createOption(optionDefinition[0], optionDefinition[1]);
      });
      return createObject(obj);
    };

    var createInitialState = function createInitialState(options) {
      return {
        // model
        items: [],

        // options
        options: createOptions(options)
      };
    };

    var fromCamels = function fromCamels(string) {
      var separator =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '-';
      return string
        .split(/(?=[A-Z])/)
        .map(function(part) {
          return part.toLowerCase();
        })
        .join(separator);
    };

    var createOptionAPI = function createOptionAPI(store, options) {
      var obj = {};
      forin(options, function(key) {
        obj[key] = {
          get: function get() {
            return store.getState().options[key];
          },
          set: function set(value) {
            store.dispatch('SET_' + fromCamels(key, '_').toUpperCase(), {
              value: value
            });
          }
        };
      });
      return obj;
    };

    var createOptionActions = function createOptionActions(options) {
      return function(dispatch, query, state) {
        var obj = {};
        forin(options, function(key) {
          var name = fromCamels(key, '_').toUpperCase();
          obj['SET_' + name] = function(action) {
            try {
              state.options[key] = action.value;
            } catch (e) {}
            // nope, failed

            // we successfully set the value of this option
            dispatch('DID_SET_' + name, { value: state.options[key] });
          };
        });
        return obj;
      };
    };

    var createOptionQueries = function createOptionQueries(options) {
      return function(state) {
        var obj = {};
        forin(options, function(key) {
          obj['GET_' + fromCamels(key, '_').toUpperCase()] = function(action) {
            return state.options[key];
          };
        });
        return obj;
      };
    };

    var InteractionMethod = {
      API: 1,
      DROP: 2,
      BROWSE: 3,
      PASTE: 4,
      NONE: 5
    };

    var getUniqueId = function getUniqueId() {
      return Math.random()
        .toString(36)
        .substr(2, 9);
    };

    var forEachDelayed = function forEachDelayed(items, cb) {
      var delay =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 75;
      return items.map(function(item, index) {
        return new Promise(function(resolve, reject) {
          setTimeout(function() {
            cb(item);
            resolve();
          }, delay * index);
        });
      });
    };

    var arrayRemove = function arrayRemove(arr, index) {
      return arr.splice(index, 1);
    };

    var on = function on() {
      var listeners = [];
      var off = function off(event, cb) {
        arrayRemove(
          listeners,
          listeners.findIndex(function(listener) {
            return listener.event === event && (listener.cb === cb || !cb);
          })
        );
      };
      return {
        fire: function fire(event) {
          for (
            var _len = arguments.length,
              args = Array(_len > 1 ? _len - 1 : 0),
              _key = 1;
            _key < _len;
            _key++
          ) {
            args[_key - 1] = arguments[_key];
          }

          listeners
            .filter(function(listener) {
              return listener.event === event;
            })
            .map(function(listener) {
              return listener.cb;
            })
            .forEach(function(cb) {
              setTimeout(function() {
                cb.apply(undefined, args);
              }, 0);
            });
        },
        on: function on(event, cb) {
          listeners.push({ event: event, cb: cb });
        },
        onOnce: function onOnce(event, _cb) {
          listeners.push({
            event: event,
            cb: function cb() {
              off(event, _cb);
              _cb.apply(undefined, arguments);
            }
          });
        },
        off: off
      };
    };

    var copyObjectPropertiesToObject = function copyObjectPropertiesToObject(
      src,
      target,
      excluded
    ) {
      Object.getOwnPropertyNames(src)
        .filter(function(property) {
          return !excluded.includes(property);
        })
        .forEach(function(key) {
          return Object.defineProperty(
            target,
            key,
            Object.getOwnPropertyDescriptor(src, key)
          );
        });
    };

    var PRIVATE = [
      'fire',
      'process',
      'revert',
      'load',
      'on',
      'off',
      'onOnce',
      'retryLoad',
      'extend',
      'archive',
      'archived',
      'release',
      'released',
      'requestProcessing'
    ];

    var createItemAPI = function createItemAPI(item) {
      var api = {};
      copyObjectPropertiesToObject(item, api, PRIVATE);
      return api;
    };

    var removeReleasedItems = function removeReleasedItems(items) {
      items.forEach(function(item, index) {
        if (item.released) {
          arrayRemove(items, index);
        }
      });
    };

    var getNonNumeric = function getNonNumeric(str) {
      return /[^0-9]+/.exec(str);
    };

    var getDecimalSeparator = function getDecimalSeparator() {
      return getNonNumeric((1.1).toLocaleString())[0];
    };

    var getThousandsSeparator = function getThousandsSeparator() {
      // Added for browsers that do not return the thousands separator (happend on native browser Android 4.4.4)
      // We check against the normal toString output and if they're the same return a comma when decimal separator is a dot
      var decimalSeparator = getDecimalSeparator();
      var thousandsStringWithSeparator = (1000.0).toLocaleString();
      var thousandsStringWithoutSeparator = (1000.0).toString();
      if (thousandsStringWithSeparator !== thousandsStringWithoutSeparator) {
        return getNonNumeric(thousandsStringWithSeparator)[0];
      }
      return decimalSeparator === '.' ? ',' : '.';
    };

    var Type = {
      BOOLEAN: 'boolean',
      INT: 'int',
      STRING: 'string',
      ARRAY: 'array',
      OBJECT: 'object',
      FUNCTION: 'function',
      ACTION: 'action',
      SERVER_API: 'serverapi',
      REGEX: 'regex'
    };

    // all registered filters
    var filters = [];

    // loops over matching filters and passes options to each filter, returning the mapped results
    var applyFilterChain = function applyFilterChain(key, value, utils) {
      return new Promise(function(resolve, reject) {
        // find matching filters for this key
        var matchingFilters = filters
          .filter(function(f) {
            return f.key === key;
          })
          .map(function(f) {
            return f.cb;
          });

        // resolve now
        if (matchingFilters.length === 0) {
          resolve(value);
          return;
        }

        // first filter to kick things of
        var initialFilter = matchingFilters.shift();

        // chain filters
        matchingFilters
          .reduce(
            // loop over promises passing value to next promise
            function(current, next) {
              return current.then(function(value) {
                return next(value, utils);
              });
            },

            // call initial filter, will return a promise
            initialFilter(value, utils)

            // all executed
          )
          .then(function(value) {
            return resolve(value);
          })
          .catch(function(error) {
            return reject(error);
          });
      });
    };

    var applyFilters = function applyFilters(key, value, utils) {
      return filters
        .filter(function(f) {
          return f.key === key;
        })
        .map(function(f) {
          return f.cb(value, utils);
        });
    };

    // adds a new filter to the list
    var addFilter = function addFilter(key, cb) {
      return filters.push({ key: key, cb: cb });
    };

    var extendDefaultOptions = function extendDefaultOptions(additionalOptions) {
      return Object.assign(defaultOptions, additionalOptions);
    };

    var getOptions$1 = function getOptions() {
      return Object.assign({}, defaultOptions);
    };

    var setOptions$1 = function setOptions(opts) {
      forin(opts, function(key, value) {
        // key does not exist, so this option cannot be set
        if (!defaultOptions[key]) {
          return;
        }
        defaultOptions[key][0] = getValueByType(
          value,
          defaultOptions[key][0],
          defaultOptions[key][1]
        );
      });
    };

    // default options on app
    var defaultOptions = {
      // the id to add to the root element
      id: [null, Type.STRING],

      // input field name to use
      name: ['filepond', Type.STRING],

      // classname to put on wrapper
      className: [null, Type.STRING],

      // is the field required
      required: [false, Type.BOOLEAN],

      // Allow media capture when value is set
      captureMethod: [null, Type.STRING],
      // - "camera", "microphone" or "camcorder",
      // - Does not work with multiple on apple devices
      // - If set, acceptedFileTypes must be made to match with media wildcard "image/*", "audio/*" or "video/*"

      // Feature toggles
      allowDrop: [true, Type.BOOLEAN], // Allow dropping of files
      allowBrowse: [true, Type.BOOLEAN], // Allow browsing the file system
      allowPaste: [true, Type.BOOLEAN], // Allow pasting files
      allowMultiple: [false, Type.BOOLEAN], // Allow multiple files (disabled by default, as multiple attribute is also required on input to allow multiple)
      allowReplace: [true, Type.BOOLEAN], // Allow dropping a file on other file to replace it (only works when multiple is set to false)
      allowRevert: [true, Type.BOOLEAN], // Allows user to revert file upload
      // TODO: allowDrag: [true, Type.BOOLEAN],					// Allow dragging files
      // TODO: allowSwipe: [true, Type.BOOLEAN],					// Allow swipe to remove files
      // TODO: allowRemoveAll: [true, Type.BOOLEAN],				// Allow removing all items at once
      // TODO: allowUploadAll: [true, Type.BOOLEAN],				// Allow uploading all items at once

      // Input requirements
      maxFiles: [null, Type.INT], // Max number of files

      // Drag 'n Drop related
      dropOnPage: [false, Type.BOOLEAN], // Allow dropping of files anywhere on page (prevents browser from opening file if dropped outside of Up)
      dropOnElement: [true, Type.BOOLEAN], // Drop needs to happen on element (set to false to also load drops outside of Up)
      dropValidation: [false, Type.BOOLEAN], // Enable or disable validating files on drop
      ignoredFiles: [['.ds_store', 'thumbs.db', 'desktop.ini'], Type.ARRAY],

      // Upload related
      instantUpload: [true, Type.BOOLEAN], // Should upload files immidiately on drop
      maxParallelUploads: [2, Type.INT], // Maximum files to upload in parallel
      // TODO: chunks: [false, Type.BOOLEAN],	// Use chunk uploading
      // TODO: chunkSize: [.5 * (1024 * 1024), Type.INT],	// Upload in 512KB chunks

      // The server api end points to use for uploading (see docs)
      server: [null, Type.SERVER_API],

      // Labels and status messages
      labelDecimalSeparator: [getDecimalSeparator(), Type.STRING], // Default is locale separator
      labelThousandsSeparator: [getThousandsSeparator(), Type.STRING], // Default is locale separator

      labelIdle: [
        'Drag & Drop your files or <span class="filepond--label-action">Browse</span>',
        Type.STRING
      ],

      labelFileWaitingForSize: ['Waiting for size', Type.STRING],
      labelFileSizeNotAvailable: ['Size not available', Type.STRING],
      labelFileCountSingular: ['file in list', Type.STRING],
      labelFileCountPlural: ['files in list', Type.STRING],
      labelFileLoading: ['Loading', Type.STRING],
      labelFileAdded: ['Added', Type.STRING], // assistive only
      labelFileRemoved: ['Removed', Type.STRING], // assistive only
      labelFileLoadError: ['Error during load', Type.STRING],
      labelFileProcessing: ['Uploading', Type.STRING],
      labelFileProcessingComplete: ['Upload complete', Type.STRING],
      labelFileProcessingAborted: ['Upload cancelled', Type.STRING],
      labelFileProcessingError: ['Error during upload', Type.STRING],
      // labelFileProcessingPaused: ['Upload paused', Type.STRING],

      labelTapToCancel: ['tap to cancel', Type.STRING],
      labelTapToRetry: ['tap to retry', Type.STRING],
      labelTapToUndo: ['tap to undo', Type.STRING],
      // labelTapToPause: ['tap to pause', Type.STRING],
      // labelTapToResume: ['tap to resume', Type.STRING],

      labelButtonRemoveItem: ['Remove', Type.STRING],
      labelButtonAbortItemLoad: ['Abort', Type.STRING],
      labelButtonRetryItemLoad: ['Retry', Type.STRING],
      labelButtonAbortItemProcessing: ['Cancel', Type.STRING],
      labelButtonUndoItemProcessing: ['Undo', Type.STRING],
      labelButtonRetryItemProcessing: ['Retry', Type.STRING],
      labelButtonProcessItem: ['Upload', Type.STRING],

      // make sure width and height plus viewpox are even numbers so icons are nicely centered
      iconRemove: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M11.586 13l-2.293 2.293a1 1 0 0 0 1.414 1.414L13 14.414l2.293 2.293a1 1 0 0 0 1.414-1.414L14.414 13l2.293-2.293a1 1 0 0 0-1.414-1.414L13 11.586l-2.293-2.293a1 1 0 0 0-1.414 1.414L11.586 13z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING
      ],
      iconProcess: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M14 10.414v3.585a1 1 0 0 1-2 0v-3.585l-1.293 1.293a1 1 0 0 1-1.414-1.415l3-3a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1-1.414 1.415L14 10.414zM9 18a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2H9z" fill="currentColor" fill-rule="evenodd"/></svg>',
        Type.STRING
      ],
      iconRetry: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M10.81 9.185l-.038.02A4.997 4.997 0 0 0 8 13.683a5 5 0 0 0 5 5 5 5 0 0 0 5-5 1 1 0 0 1 2 0A7 7 0 1 1 9.722 7.496l-.842-.21a.999.999 0 1 1 .484-1.94l3.23.806c.535.133.86.675.73 1.21l-.804 3.233a.997.997 0 0 1-1.21.73.997.997 0 0 1-.73-1.21l.23-.928v-.002z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING
      ],
      iconUndo: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M9.185 10.81l.02-.038A4.997 4.997 0 0 1 13.683 8a5 5 0 0 1 5 5 5 5 0 0 1-5 5 1 1 0 0 0 0 2A7 7 0 1 0 7.496 9.722l-.21-.842a.999.999 0 1 0-1.94.484l.806 3.23c.133.535.675.86 1.21.73l3.233-.803a.997.997 0 0 0 .73-1.21.997.997 0 0 0-1.21-.73l-.928.23-.002-.001z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING
      ],
      iconDone: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M18.293 9.293a1 1 0 0 1 1.414 1.414l-7.002 7a1 1 0 0 1-1.414 0l-3.998-4a1 1 0 1 1 1.414-1.414L12 15.586l6.294-6.293z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING
      ],

      // event handlers
      oninit: [null, Type.FUNCTION],
      onwarning: [null, Type.FUNCTION],
      onerror: [null, Type.FUNCTION],
      onaddfilestart: [null, Type.FUNCTION],
      onaddfileprogress: [null, Type.FUNCTION],
      onaddfile: [null, Type.FUNCTION],
      onprocessfilestart: [null, Type.FUNCTION],
      onprocessfileprogress: [null, Type.FUNCTION],
      onprocessfileabort: [null, Type.FUNCTION],
      onprocessfilerevert: [null, Type.FUNCTION],
      onprocessfile: [null, Type.FUNCTION],
      onremovefile: [null, Type.FUNCTION],
      onpreparefile: [null, Type.FUNCTION],
      onupdatefiles: [null, Type.FUNCTION],

      // hooks
      beforeAddFile: [null, Type.FUNCTION],
      beforeRemoveFile: [null, Type.FUNCTION],

      // styles
      stylePanelLayout: [null, Type.STRING], // null 'integrated', 'compact', 'circle'
      stylePanelAspectRatio: [null, Type.STRING], // null or '3:2' or 1
      styleButtonRemoveItemPosition: ['left', Type.STRING],
      styleButtonProcessItemPosition: ['right', Type.STRING],
      styleLoadIndicatorPosition: ['right', Type.STRING],
      styleProgressIndicatorPosition: ['right', Type.STRING],

      // custom initial files array
      files: [[], Type.ARRAY]
    };

    var getItemByQuery = function getItemByQuery(items, query) {
      // just return first index
      if (isEmpty(query)) {
        return items[0] || null;
      }

      // query is index
      if (isInt(query)) {
        return items[query] || null;
      }

      // if query is item, get the id
      if (
        (typeof query === 'undefined' ? 'undefined' : _typeof(query)) === 'object'
      ) {
        query = query.id;
      }

      // assume query is a string and return item by id
      return (
        items.find(function(item) {
          return item.id === query;
        }) || null
      );
    };

    var getNumericAspectRatioFromString = function getNumericAspectRatioFromString(
      aspectRatio
    ) {
      if (isEmpty(aspectRatio)) {
        return aspectRatio;
      }
      if (/:/.test(aspectRatio)) {
        var _aspectRatio$split = aspectRatio.split(':'),
          _aspectRatio$split2 = slicedToArray(_aspectRatio$split, 2),
          w = _aspectRatio$split2[0],
          h = _aspectRatio$split2[1];

        return h / w;
      }
      return parseFloat(aspectRatio);
    };

    var getActiveItems = function getActiveItems(items) {
      return items.filter(function(item) {
        return !item.archived;
      });
    };

    var ItemStatus = {
      INIT: 1,
      IDLE: 2,
      PROCESSING_QUEUED: 9,
      PROCESSING: 3,
      PROCESSING_PAUSED: 4,
      PROCESSING_COMPLETE: 5,
      PROCESSING_ERROR: 6,
      LOADING: 7,
      LOAD_ERROR: 8
    };

    var queries = function queries(state) {
      return {
        GET_ITEM: function GET_ITEM(query) {
          return getItemByQuery(state.items, query);
        },

        GET_ACTIVE_ITEM: function GET_ACTIVE_ITEM(query) {
          return getItemByQuery(getActiveItems(state.items), query);
        },

        GET_ACTIVE_ITEMS: function GET_ACTIVE_ITEMS(query) {
          return getActiveItems(state.items);
        },

        GET_ITEMS: function GET_ITEMS(query) {
          return state.items;
        },

        GET_ITEM_NAME: function GET_ITEM_NAME(query) {
          var item = getItemByQuery(state.items, query);
          return item ? item.filename : null;
        },

        GET_ITEM_SIZE: function GET_ITEM_SIZE(query) {
          var item = getItemByQuery(state.items, query);
          return item ? item.fileSize : null;
        },

        GET_STYLES: function GET_STYLES() {
          return Object.keys(state.options)
            .filter(function(key) {
              return /^style/.test(key);
            })
            .map(function(option) {
              return {
                name: option,
                value: state.options[option]
              };
            });
        },

        GET_PANEL_ASPECT_RATIO: function GET_PANEL_ASPECT_RATIO() {
          var isShapeCircle = /circle/.test(state.options.stylePanelLayout);
          var aspectRatio = isShapeCircle
            ? 1
            : getNumericAspectRatioFromString(
                state.options.stylePanelAspectRatio
              );
          return aspectRatio;
        },

        GET_ITEMS_BY_STATUS: function GET_ITEMS_BY_STATUS(status) {
          return getActiveItems(state.items).filter(function(item) {
            return item.status === status;
          });
        },

        GET_TOTAL_ITEMS: function GET_TOTAL_ITEMS() {
          return getActiveItems(state.items).length;
        },

        IS_ASYNC: function IS_ASYNC() {
          return (
            isObject(state.options.server) &&
            (isObject(state.options.server.process) ||
              isFunction(state.options.server.process))
          );
        }
      };
    };

    var hasRoomForItem = function hasRoomForItem(state) {
      var count = getActiveItems(state.items).length;

      // if cannot have multiple items, to add one item it should currently not contain items
      if (!state.options.allowMultiple) {
        return count === 0;
      }

      // if allows multiple items, we check if a max item count has been set, if not, there's no limit
      var maxFileCount = state.options.maxFiles;
      if (maxFileCount === null) {
        return true;
      }

      // we check if the current count is smaller than the max count, if so, another file can still be added
      if (count < maxFileCount) {
        return true;
      }

      // no more room for another file
      return false;
    };

    var limit = function limit(value, min, max) {
      return Math.max(Math.min(max, value), min);
    };

    var arrayInsert = function arrayInsert(arr, index, item) {
      return arr.splice(index, 0, item);
    };

    var insertItem = function insertItem(items, item, index) {
      if (isEmpty(item)) {
        return null;
      }

      // if index is undefined, append
      if (typeof index === 'undefined') {
        items.push(item);
        return item;
      }

      // limit the index to the size of the items array
      index = limit(index, 0, items.length);

      // add item to array
      arrayInsert(items, index, item);

      // expose
      return item;
    };

    var isBase64DataURI = function isBase64DataURI(str) {
      return /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*)\s*$/i.test(
        str
      );
    };

    var getFilenameFromURL = function getFilenameFromURL(url) {
      return url
        .split('/')
        .pop()
        .split('?')
        .shift();
    };

    var getExtensionFromFilename = function getExtensionFromFilename(name) {
      return name.split('.').pop();
    };

    var guesstimateExtension = function guesstimateExtension(type) {
      // if no extension supplied, exit here
      if (typeof type !== 'string') {
        return '';
      }

      // get subtype
      var subtype = type.split('/').pop();

      // is svg subtype
      if (/svg/.test(subtype)) {
        return 'svg';
      }

      if (/zip|compressed/.test(subtype)) {
        return 'zip';
      }

      if (/plain/.test(subtype)) {
        return 'txt';
      }

      if (/msword/.test(subtype)) {
        return 'doc';
      }

      // if is valid subtype
      if (/[a-z]+/.test(subtype)) {
        // always use jpg extension
        if (subtype === 'jpeg') {
          return 'jpg';
        }

        // return subtype
        return subtype;
      }

      return '';
    };

    var leftPad = function leftPad(value) {
      var padding =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
      return (padding + value).slice(-padding.length);
    };

    var getDateString = function getDateString() {
      var date =
        arguments.length > 0 && arguments[0] !== undefined
          ? arguments[0]
          : new Date();
      return (
        date.getFullYear() +
        '-' +
        leftPad(date.getMonth() + 1, '00') +
        '-' +
        leftPad(date.getDate(), '00') +
        '_' +
        leftPad(date.getHours(), '00') +
        '-' +
        leftPad(date.getMinutes(), '00') +
        '-' +
        leftPad(date.getSeconds(), '00')
      );
    };

    var getFileFromBlob = function getFileFromBlob(blob, filename) {
      var type =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
      var extension =
        arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

      var file =
        typeof type === 'string'
          ? blob.slice(0, blob.size, type)
          : blob.slice(0, blob.size, blob.type);
      file.lastModifiedDate = new Date();

      // if blob has name property, use as filename if no filename supplied
      if (!isString(filename)) {
        filename = getDateString();
      }

      // if filename supplied but no extension and filename has extension
      if (filename && extension === null && getExtensionFromFilename(filename)) {
        file.name = filename;
      } else {
        extension = extension || guesstimateExtension(file.type);
        file.name = filename + (extension ? '.' + extension : '');
      }

      return file;
    };

    var getBlobBuilder = function getBlobBuilder() {
      return (window.BlobBuilder =
        window.BlobBuilder ||
        window.WebKitBlobBuilder ||
        window.MozBlobBuilder ||
        window.MSBlobBuilder);
    };

    var createBlob = function createBlob(arrayBuffer, mimeType) {
      var BB = getBlobBuilder();

      if (BB) {
        var bb = new BB();
        bb.append(arrayBuffer);
        return bb.getBlob(mimeType);
      }

      return new Blob([arrayBuffer], {
        type: mimeType
      });
    };

    var getBlobFromByteStringWithMimeType = function getBlobFromByteStringWithMimeType(
      byteString,
      mimeType
    ) {
      var ab = new ArrayBuffer(byteString.length);
      var ia = new Uint8Array(ab);

      for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }

      return createBlob(ab, mimeType);
    };

    var getMimeTypeFromBase64DataURI = function getMimeTypeFromBase64DataURI(
      dataURI
    ) {
      return (/^data:(.+);/.exec(dataURI) || [])[1] || null;
    };

    var getBase64DataFromBase64DataURI = function getBase64DataFromBase64DataURI(
      dataURI
    ) {
      // get data part of string (remove data:image/jpeg...,)
      var data = dataURI.split(',')[1];

      // remove any whitespace as that causes InvalidCharacterError in IE
      return data.replace(/\s/g, '');
    };

    var getByteStringFromBase64DataURI = function getByteStringFromBase64DataURI(
      dataURI
    ) {
      return atob(getBase64DataFromBase64DataURI(dataURI));
    };

    var getBlobFromBase64DataURI = function getBlobFromBase64DataURI(dataURI) {
      var mimeType = getMimeTypeFromBase64DataURI(dataURI);
      var byteString = getByteStringFromBase64DataURI(dataURI);

      return getBlobFromByteStringWithMimeType(byteString, mimeType);
    };

    var getFileFromBase64DataURI = function getFileFromBase64DataURI(
      dataURI,
      filename,
      extension
    ) {
      return getFileFromBlob(
        getBlobFromBase64DataURI(dataURI),
        filename,
        null,
        extension
      );
    };

    var getFileNameFromHeader = function getFileNameFromHeader(header) {
      var matches = header.match(/(?:filename="(.+)")|(?:filename=(.+))/) || [];
      return matches[1] || matches[2];
    };

    var getFileSizeFromHeader = function getFileSizeFromHeader(header) {
      if (/content-length:/i.test(header)) {
        var size = header.match(/[0-9]+/)[0];
        return size ? parseInt(size, 10) : null;
      }
      return null;
    };

    var getTranfserIdFromHeader = function getTranfserIdFromHeader(header) {
      if (/x-content-transfer-id:/i.test(header)) {
        var id = (header.split(':')[1] || '').trim();
        return id || null;
      }
      return null;
    };

    var getFileInfoFromHeaders = function getFileInfoFromHeaders(headers) {
      var info = {
        source: null,
        name: null,
        size: null
      };

      var rows = headers.split('\n');
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (
          var _iterator = rows[Symbol.iterator](), _step;
          !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
          _iteratorNormalCompletion = true
        ) {
          var header = _step.value;

          var name = getFileNameFromHeader(header);
          if (name) {
            info.name = name;
            continue;
          }

          var size = getFileSizeFromHeader(header);
          if (size) {
            info.size = size;
            continue;
          }

          var source = getTranfserIdFromHeader(header);
          if (source) {
            info.source = source;
            continue;
          }
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      return info;
    };

    var createFileLoader = function createFileLoader(fetchFn) {
      var state = {
        source: null,
        complete: false,
        progress: 0,
        size: null,
        timestamp: null,
        duration: 0,
        request: null
      };

      var getProgress = function getProgress() {
        return state.progress;
      };
      var abort = function abort() {
        if (!state.request) {
          return;
        }
        state.request.abort();
      };

      // load source
      var load = function load() {
        // get quick reference
        var source = state.source;

        api.fire('init', source);

        // Load Files
        if (source instanceof File) {
          api.fire('load', source);
        } else if (source instanceof Blob) {
          // Load blobs, set default name to current date
          api.fire('load', getFileFromBlob(source, source.name));
        } else if (isBase64DataURI(source)) {
          // Load base 64, set default name to current date
          api.fire('load', getFileFromBase64DataURI(source));
        } else {
          // Deal as if is external URL, let's load it!
          loadURL(source);
        }
      };

      // loads a url
      var loadURL = function loadURL(url) {
        // is remote url and no fetch method supplied
        if (!fetchFn) {
          api.fire('error', {
            type: 'error',
            body: "Can't load URL",
            code: 400
          });
          return;
        }

        // set request start
        state.timestamp = Date.now();

        // load file
        state.request = fetchFn(
          url,
          function(response) {
            // update duration
            state.duration = Date.now() - state.timestamp;

            // done!
            state.complete = true;

            // turn blob response into a file
            if (response instanceof Blob) {
              response = getFileFromBlob(response, getFilenameFromURL(url));
            }

            api.fire('load', response instanceof Blob ? response : response.body);
          },
          function(error) {
            api.fire(
              'error',
              typeof error === 'string'
                ? {
                    type: 'error',
                    code: 0,
                    body: error
                  }
                : error
            );
          },
          function(computable, current, total) {
            // collected some meta data already
            if (total) {
              state.size = total;
            }

            // update duration
            state.duration = Date.now() - state.timestamp;

            // if we can't compute progress, we're not going to fire progress events
            if (!computable) {
              state.progress = null;
              return;
            }

            // update progress percentage
            state.progress = current / total;

            // expose
            api.fire('progress', state.progress);
          },
          function() {
            api.fire('abort');
          },
          function(response) {
            var fileinfo = getFileInfoFromHeaders(
              typeof response === 'string' ? response : response.headers
            );
            api.fire('meta', {
              size: state.size || fileinfo.size,
              filename: fileinfo.name,
              source: fileinfo.source
            });
          }
        );
      };

      var api = Object.assign({}, on(), {
        setSource: function setSource(source) {
          return (state.source = source);
        },
        getProgress: getProgress, // file load progress
        abort: abort, // abort file load
        load: load // start load
      });

      return api;
    };

    var isGet = function isGet(method) {
      return /GET|HEAD/.test(method);
    };

    var sendRequest = function sendRequest(data, url, options) {
      var api = {
        onheaders: function onheaders() {},
        onprogress: function onprogress() {},
        onload: function onload() {},
        ontimeout: function ontimeout() {},
        onerror: function onerror() {},
        onabort: function onabort() {},
        abort: function abort() {
          aborted = true;
          xhr.abort();
        }
      };

      // timeout identifier, only used when timeout is defined
      var aborted = false;
      var headersReceived = false;

      // set default options
      options = Object.assign(
        {
          method: 'POST',
          headers: {},
          withCredentials: false
        },
        options
      );

      // encode url
      url = encodeURI(url);

      // if method is GET, add any received data to url

      if (isGet(options.method) && data) {
        url =
          '' +
          url +
          encodeURIComponent(
            typeof data === 'string' ? data : JSON.stringify(data)
          );
      }

      // create request
      var xhr = new XMLHttpRequest();

      // progress of load
      var process = isGet(options.method) ? xhr : xhr.upload;
      process.onprogress = function(e) {
        // no progress event when aborted ( onprogress is called once after abort() )
        if (aborted) {
          return;
        }

        api.onprogress(e.lengthComputable, e.loaded, e.total);
      };

      // tries to get header info to the app as fast as possible
      xhr.onreadystatechange = function() {
        // not interesting in these states ('unsent' and 'openend' as they don't give us any additional info)
        if (xhr.readyState < 2) {
          return;
        }

        // no server response
        if (xhr.readyState === 4 && xhr.status === 0) {
          return;
        }

        if (headersReceived) {
          return;
        }

        headersReceived = true;

        // we've probably received some useful data in response headers
        api.onheaders(xhr);
      };

      // load successful
      xhr.onload = function() {
        // is classified as valid response
        if (xhr.status >= 200 && xhr.status < 300) {
          api.onload(xhr);
        } else {
          api.onerror(xhr);
        }
      };

      // error during load
      xhr.onerror = function() {
        return api.onerror(xhr);
      };

      // request aborted
      xhr.onabort = function() {
        aborted = true;
        api.onabort();
      };

      // request timeout
      xhr.ontimeout = function() {
        return api.ontimeout(xhr);
      };

      // open up open up!
      xhr.open(options.method, url, true);

      // set timeout if defined (do it after open so IE11 plays ball)
      if (isInt(options.timeout)) {
        xhr.timeout = options.timeout;
      }

      // add headers
      Object.keys(options.headers).forEach(function(key) {
        xhr.setRequestHeader(key, options.headers[key]);
      });

      // set type of response
      if (options.responseType) {
        xhr.responseType = options.responseType;
      }

      // set credentials
      if (options.withCredentials) {
        xhr.withCredentials = true;
      }

      // let's send our data
      xhr.send(data);

      return api;
    };

    var createResponse = function createResponse(type, code, body, headers) {
      return {
        type: type,
        code: code,
        body: body,
        headers: headers
      };
    };

    var createTimeoutResponse = function createTimeoutResponse(cb) {
      return function(xhr) {
        cb(createResponse('error', 0, 'Timeout', xhr.getAllResponseHeaders()));
      };
    };

    var createFetchFunction = function createFetchFunction() {
      var apiUrl =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
      var action = arguments[1];

      // custom handler (should also handle file, load, error, progress and abort)
      if (typeof action === 'function') {
        return action;
      }

      // no action supplied
      if (!action || !isString(action.url)) {
        return null;
      }

      // set onload hanlder
      var onload =
        action.onload ||
        function(res) {
          return res;
        };
      var onerror =
        action.onerror ||
        function(res) {
          return null;
        };

      // internal handler
      return function(url, load, error, progress, abort, headers) {
        // do local or remote request based on if the url is external
        var request = sendRequest(
          url,
          apiUrl + action.url,
          Object.assign({}, action, {
            responseType: 'blob'
          })
        );

        request.onload = function(xhr) {
          // get headers
          var headers = xhr.getAllResponseHeaders();

          // get filename
          var filename =
            getFileInfoFromHeaders(headers).name || getFilenameFromURL(url);

          // create response
          load(
            createResponse(
              'load',
              xhr.status,
              getFileFromBlob(onload(xhr.response), filename),
              headers
            )
          );
        };

        request.onerror = function(xhr) {
          error(
            createResponse(
              'error',
              xhr.status,
              onerror(xhr.response) || xhr.statusText,
              xhr.getAllResponseHeaders()
            )
          );
        };

        request.onheaders = function(xhr) {
          headers(
            createResponse(
              'headers',
              xhr.status,
              null,
              xhr.getAllResponseHeaders()
            )
          );
        };

        request.ontimeout = createTimeoutResponse(error);
        request.onprogress = progress;
        request.onabort = abort;

        // should return request
        return request;
      };
    };

    /*
  function signature:
    (file, metadata, load, error, progress, abort) => {
      return {
      abort:() => {}
    }
  }
  */
    var createProcessorFunction = function createProcessorFunction() {
      var apiUrl =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
      var action = arguments[1];
      var name = arguments[2];

      // custom handler (should also handle file, load, error, progress and abort)
      if (typeof action === 'function') {
        return function() {
          for (
            var _len = arguments.length, params = Array(_len), _key = 0;
            _key < _len;
            _key++
          ) {
            params[_key] = arguments[_key];
          }

          return action.apply(undefined, [name].concat(params));
        };
      }

      // no action supplied
      if (!action || !isString(action.url)) {
        return null;
      }

      // internal handler
      return function(file, metadata, load, error, progress, abort) {
        // set onload hanlder
        var ondata =
          action.ondata ||
          function(fd) {
            return fd;
          };
        var onload =
          action.onload ||
          function(res) {
            return res;
          };
        var onerror =
          action.onerror ||
          function(res) {
            return null;
          };

        // no file received
        if (!file) return;

        // create formdata object
        var formData = new FormData();

        // add metadata under same name
        if (isObject(metadata)) {
          formData.append(name, JSON.stringify(metadata));
        }

        // Turn into an array of objects so no matter what the input, we can handle it the same way
        (file instanceof Blob ? [{ name: null, file: file }] : file).forEach(
          function(item) {
            formData.append(
              name,
              item.file,
              item.name === null
                ? item.file.name
                : '' + item.name + item.file.name
            );
          }
        );

        // send request object
        var request = sendRequest(ondata(formData), apiUrl + action.url, action);
        request.onload = function(xhr) {
          load(
            createResponse(
              'load',
              xhr.status,
              onload(xhr.response),
              xhr.getAllResponseHeaders()
            )
          );
        };

        request.onerror = function(xhr) {
          error(
            createResponse(
              'error',
              xhr.status,
              onerror(xhr.response) || xhr.statusText,
              xhr.getAllResponseHeaders()
            )
          );
        };

        request.ontimeout = createTimeoutResponse(error);
        request.onprogress = progress;
        request.onabort = abort;

        // should return request
        return request;
      };
    };

    /*
   function signature:
   (uniqueFileId, load, error) => { }
   */
    var createRevertFunction = function createRevertFunction() {
      var apiUrl =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
      var action = arguments[1];

      // is custom implementation
      if (typeof action === 'function') {
        return action;
      }

      // no action supplied, return stub function, interface will work, but file won't be removed
      if (!action || !isString(action.url)) {
        return function(uniqueFileId, load) {
          return load();
        };
      }

      // set onload hanlder
      var onload =
        action.onload ||
        function(res) {
          return res;
        };
      var onerror =
        action.onerror ||
        function(res) {
          return null;
        };

      // internal implementation
      return function(uniqueFileId, load, error) {
        var request = sendRequest(
          uniqueFileId,
          apiUrl + action.url,
          action // contains method, headers and withCredentials properties
        );
        request.onload = function(xhr) {
          load(
            createResponse(
              'load',
              xhr.status,
              onload(xhr.response),
              xhr.getAllResponseHeaders()
            )
          );
        };

        request.onerror = function(xhr) {
          error(
            createResponse(
              'error',
              xhr.status,
              onerror(xhr.response) || xhr.statusText,
              xhr.getAllResponseHeaders()
            )
          );
        };

        request.ontimeout = createTimeoutResponse(error);

        return request;
      };
    };

    var getRandomNumber = function getRandomNumber() {
      var min =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      var max =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
      return min + Math.random() * (max - min);
    };

    var createPerceivedPerformanceUpdater = function createPerceivedPerformanceUpdater(
      cb
    ) {
      var duration =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
      var tickMin =
        arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 25;
      var tickMax =
        arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 250;

      var timeout = null;
      var start = Date.now();

      var tick = function tick() {
        var runtime = Date.now() - start;
        var delay = getRandomNumber(tickMin, tickMax);

        if (runtime + delay > duration) {
          delay = runtime + delay - duration;
        }

        var progress = runtime / duration;
        if (progress >= 1) {
          cb(1);
          return;
        }

        cb(progress);

        timeout = setTimeout(tick, delay);
      };

      tick();

      return {
        clear: function clear() {
          clearTimeout(timeout);
        }
      };
    };

    var createFileProcessor = function createFileProcessor(processFn) {
      var state = {
        complete: false,
        perceivedProgress: 0,
        perceivedPerformanceUpdater: null,
        progress: null,
        timestamp: null,
        perceivedDuration: 0,
        duration: 0,
        request: null,
        response: null
      };

      var process = function process(file, metadata) {
        var progressFn = function progressFn() {
          // we've not yet started the real download, stop here
          // the request might not go through, for instance, there might be some server trouble
          // if state.progress is null, the server does not allow computing progress and we show the spinner instead
          if (state.duration === 0 || state.progress === null) {
            return;
          }

          // as we're now processing, fire the progress event
          api.fire('progress', api.getProgress());
        };

        var completeFn = function completeFn() {
          state.complete = true;

          api.fire('load-perceived', state.response.body);
        };

        // let's start processing
        api.fire('start');

        // set request start
        state.timestamp = Date.now();

        // create perceived performance progress indicator
        state.perceivedPerformanceUpdater = createPerceivedPerformanceUpdater(
          function(progress) {
            state.perceivedProgress = progress;
            state.perceivedDuration = Date.now() - state.timestamp;

            progressFn();

            // if fake progress is done, and a response has been received,
            // and we've not yet called the complete method
            if (
              state.response &&
              state.perceivedProgress === 1 &&
              !state.complete
            ) {
              // we done!
              completeFn();
            }
          },
          // random delay as in a list of files you start noticing
          // files uploading at the exact same speed
          getRandomNumber(750, 1500)
        );

        // remember request so we can abort it later
        state.request = processFn(
          // the file to process
          file,

          // the metadata to send along
          metadata,

          // callbacks (load, error, progress, abort)
          // load expects the body to be a server id if
          // you want to make use of revert
          function(response) {
            // we put the response in state so we can access
            // it outside of this method
            state.response = isObject(response)
              ? response
              : {
                  type: 'load',
                  code: 200,
                  body: '' + response,
                  headers: {}
                };

            // update duration
            state.duration = Date.now() - state.timestamp;

            // force progress to 1 as we're now done
            state.progress = 1;

            // actual load is done let's share results
            api.fire('load', state.response.body);

            // we are really done
            // if perceived progress is 1 ( wait for perceived progress to complete )
            // or if server does not support progress ( null )
            if (state.perceivedProgress === 1) {
              completeFn();
            }
          },

          // error is expected to be an object with type, code, body
          function(error) {
            // cancel updater
            state.perceivedPerformanceUpdater.clear();

            // update others about this error
            api.fire(
              'error',
              isObject(error)
                ? error
                : {
                    type: 'error',
                    code: 0,
                    body: '' + error
                  }
            );
          },

          // actual processing progress
          function(computable, current, total) {
            // update actual duration
            state.duration = Date.now() - state.timestamp;

            // update actual progress
            state.progress = computable ? current / total : null;

            progressFn();
          },

          // abort does not expect a value
          function() {
            // stop updater
            state.perceivedPerformanceUpdater.clear();

            // fire the abort event so we can switch visuals
            api.fire('abort', state.response ? state.response.body : null);
          }
        );
      };

      var abort = function abort() {
        // no request running, can't abort
        if (!state.request) {
          return;
        }

        // stop updater
        state.perceivedPerformanceUpdater.clear();

        // abort actual request
        state.request.abort();

        // if has response object, we've completed the request
        state.complete = true;
      };

      var reset = function reset() {
        abort();
        state.complete = false;
        state.perceivedProgress = 0;
        state.progress = 0;
        state.timestamp = null;
        state.perceivedDuration = 0;
        state.duration = 0;
        state.request = null;
        state.response = null;
      };

      var getProgress = function getProgress() {
        return state.progress
          ? Math.min(state.progress, state.perceivedProgress)
          : null;
      };
      var getDuration = function getDuration() {
        return Math.min(state.duration, state.perceivedDuration);
      };

      var api = Object.assign({}, on(), {
        process: process, // start processing file
        abort: abort, // abort active process request
        getProgress: getProgress,
        getDuration: getDuration,
        reset: reset
      });

      return api;
    };

    var getFilenameWithoutExtension = function getFilenameWithoutExtension(name) {
      return name.substr(0, name.lastIndexOf('.')) || name;
    };

    var createFileStub = function createFileStub(source) {
      var data = [source.name, source.size, source.type];

      // is blob or base64, then we need to set the name
      if (source instanceof Blob || isBase64DataURI(source)) {
        data[0] = source.name || getDateString();
      } else if (isBase64DataURI(source)) {
        // if is base64 data uri we need to determine the average size and type
        data[1] = source.length;
        data[2] = getMimeTypeFromBase64DataURI(source);
      } else if (isString(source)) {
        // url
        data[0] = getFilenameFromURL(source);
        data[1] = 0;
        data[2] = 'application/octet-stream';
      }

      return {
        name: data[0],
        size: data[1],
        type: data[2]
      };
    };

    var FileOrigin$1 = {
      INPUT: 1,
      LIMBO: 2,
      LOCAL: 3
    };

    var deepCloneObject = function deepCloneObject(src) {
      if (!isObject(src)) return src;
      var target = isArray(src) ? [] : {};
      for (var key in src) {
        if (!src.hasOwnProperty(key)) continue;
        var v = src[key];
        target[key] = v && isObject(v) ? deepCloneObject(v) : v;
      }
      return target;
    };

    var createItem = function createItem() {
      var origin =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
      var serverFileReference =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
      var file =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

      // unique id for this item, is used to identify the item across views
      var id = getUniqueId();

      /**
       * Internal item state
       */
      var state = {
        // is archived
        archived: false,

        // removed from view
        released: false,

        // original source
        source: null,

        // file model reference
        file: file,

        // id of file on server
        serverFileReference: serverFileReference,

        // current item status
        status: serverFileReference
          ? ItemStatus.PROCESSING_COMPLETE
          : ItemStatus.INIT,

        // active processes
        activeLoader: null,
        activeProcessor: null
      };

      // callback used when abort processing is called to link back to the resolve method
      var abortProcessingRequestComplete = null;

      /**
       * Externally added item metadata
       */
      var metadata = {};

      // item data
      var setStatus = function setStatus(status) {
        return (state.status = status);
      };

      // fire event unless the item has been archived
      var fire = function fire(event) {
        for (
          var _len = arguments.length,
            params = Array(_len > 1 ? _len - 1 : 0),
            _key = 1;
          _key < _len;
          _key++
        ) {
          params[_key - 1] = arguments[_key];
        }

        if (state.released) return;
        api.fire.apply(api, [event].concat(params));
      };

      // file data
      var getFileExtension = function getFileExtension() {
        return getExtensionFromFilename(state.file.name);
      };
      var getFileType = function getFileType() {
        return state.file.type;
      };
      var getFileSize = function getFileSize() {
        return state.file.size;
      };
      var getFile = function getFile() {
        return state.file;
      };

      //
      // logic to load a file
      //
      var load = function load(source, loader, onload) {
        // remember the original item source
        state.source = source;

        // file stub is already there
        if (state.file) {
          fire('load-skip');
          return;
        }

        // set a stub file object while loading the actual data
        state.file = createFileStub(source);

        // starts loading
        loader.on('init', function() {
          fire('load-init');
        });

        // we'eve received a size indication, let's update the stub
        loader.on('meta', function(meta) {
          // set size of file stub
          state.file.size = meta.size;

          // set name of file stub
          state.file.filename = meta.filename;

          // if has received source, we done
          if (meta.source) {
            origin = FileOrigin$1.LIMBO;
            state.serverFileReference = meta.source;
            state.status = ItemStatus.PROCESSING_COMPLETE;
          }

          // size has been updated
          fire('load-meta');
        });

        // the file is now loading we need to update the progress indicators
        loader.on('progress', function(progress) {
          setStatus(ItemStatus.LOADING);

          fire('load-progress', progress);
        });

        // an error was thrown while loading the file, we need to switch to error state
        loader.on('error', function(error) {
          setStatus(ItemStatus.LOAD_ERROR);

          fire('load-request-error', error);
        });

        // user or another process aborted the file load (cannot retry)
        loader.on('abort', function() {
          setStatus(ItemStatus.INIT);

          fire('load-abort');
        });

        // done loading
        loader.on('load', function(file) {
          // as we've now loaded the file the loader is no longer required
          state.activeLoader = null;

          // called when file has loaded succesfully
          var success = function success(result) {
            // set (possibly) transformed file
            state.file = result.size > 0 ? result : state.file;

            // file received
            if (origin === FileOrigin$1.LIMBO && state.serverFileReference) {
              setStatus(ItemStatus.PROCESSING_COMPLETE);
            } else {
              setStatus(ItemStatus.IDLE);
            }

            fire('load');
          };

          var error = function error(result) {
            // set original file
            state.file = file;
            fire('load-meta');

            setStatus(ItemStatus.LOAD_ERROR);
            fire('load-file-error', result);
          };

          // if we already have a server file reference, we don't need to call the onload method
          if (state.serverFileReference) {
            success(file);
            return;
          }

          // no server id, let's give this file the full treatment
          onload(file, success, error);
        });

        // set loader source data
        loader.setSource(source);

        // set as active loader
        state.activeLoader = loader;

        // load the source data
        loader.load();
      };

      var retryLoad = function retryLoad() {
        if (!state.activeLoader) {
          return;
        }
        state.activeLoader.load();
      };

      var abortLoad = function abortLoad() {
        if (!state.activeLoader) {
          return;
        }
        state.activeLoader.abort();
      };

      //
      // logic to process a file
      //
      var process = function process(processor, onprocess) {
        // now processing
        setStatus(ItemStatus.PROCESSING);

        // reset abort callback
        abortProcessingRequestComplete = null;

        // if no file loaded we'll wait for the load event
        if (!(state.file instanceof Blob)) {
          api.on('load', function() {
            process(processor, onprocess);
          });
          return;
        }

        // setup processor
        processor.on('load', function(serverFileReference) {
          // need this id to be able to revert the upload
          state.serverFileReference = serverFileReference;
        });

        processor.on('load-perceived', function(serverFileReference) {
          // no longer required
          state.activeProcessor = null;

          // need this id to be able to rever the upload
          state.serverFileReference = serverFileReference;

          setStatus(ItemStatus.PROCESSING_COMPLETE);
          fire('process-complete', serverFileReference);
        });

        processor.on('start', function() {
          fire('process-start');
        });

        processor.on('error', function(error) {
          state.activeProcessor = null;
          setStatus(ItemStatus.PROCESSING_ERROR);
          fire('process-error', error);
        });

        processor.on('abort', function(serverFileReference) {
          state.activeProcessor = null;

          // if file was uploaded but processing was cancelled during perceived processor time store file reference
          state.serverFileReference = serverFileReference;

          setStatus(ItemStatus.IDLE);
          fire('process-abort');

          // has timeout so doesn't interfere with remove action
          if (abortProcessingRequestComplete) {
            abortProcessingRequestComplete();
          }
        });

        processor.on('progress', function(progress) {
          fire('process-progress', progress);
        });

        // when successfully transformed
        var success = function success(file) {
          // if was archived in the mean time, don't process
          if (state.archived) return;

          // process file!
          processor.process(file, Object.assign({}, metadata));
        };

        // something went wrong during transform phase
        var error = function error(result) {};

        // start processing the file
        onprocess(state.file, success, error);

        // set as active processor
        state.activeProcessor = processor;
      };

      var requestProcessing = function requestProcessing() {
        setStatus(ItemStatus.PROCESSING_QUEUED);
      };

      var abortProcessing = function abortProcessing() {
        return new Promise(function(resolve) {
          if (!state.activeProcessor) {
            setStatus(ItemStatus.IDLE);
            fire('process-abort');

            resolve();
            return;
          }

          abortProcessingRequestComplete = function abortProcessingRequestComplete() {
            resolve();
          };

          state.activeProcessor.abort();
        });
      };

      //
      // logic to revert a processed file
      //
      var revert = function revert(revertFileUpload) {
        return new Promise(function(resolve) {
          // cannot revert without a server id for this process
          if (state.serverFileReference === null) {
            resolve();
            return;
          }

          // revert the upload (fire and forget)
          revertFileUpload(
            state.serverFileReference,
            function() {
              // reset file server id as now it's no available on the server
              state.serverFileReference = null;
              resolve();
            },
            function(error) {
              resolve();
              // TODO: handle revert error
            }
          );

          // fire event
          setStatus(ItemStatus.IDLE);
          fire('process-revert');
        });
      };

      // exposed methods
      var _setMetadata = function _setMetadata(key, value, silent) {
        var keys = key.split('.');
        var root = keys[0];
        var last = keys.pop();
        var data = metadata;
        keys.forEach(function(key) {
          return (data = data[key]);
        });

        // compare old value against new value, if they're the same, we're not updating
        if (JSON.stringify(data[last]) === JSON.stringify(value)) {
          return;
        }

        // update value
        data[last] = value;

        if (silent) return;

        fire('metadata-update', {
          key: root,
          value: metadata[root]
        });
      };

      var getMetadata = function getMetadata(key) {
        return deepCloneObject(key ? metadata[key] : metadata);
      };

      var api = Object.assign(
        {
          id: {
            get: function get() {
              return id;
            }
          },
          origin: {
            get: function get() {
              return origin;
            }
          },
          serverId: {
            get: function get() {
              return state.serverFileReference;
            }
          },
          status: {
            get: function get() {
              return state.status;
            }
          },
          filename: {
            get: function get() {
              return state.file.name;
            }
          },
          filenameWithoutExtension: {
            get: function get() {
              return getFilenameWithoutExtension(state.file.name);
            }
          },
          fileExtension: { get: getFileExtension },
          fileType: { get: getFileType },
          fileSize: { get: getFileSize },
          file: { get: getFile },

          source: {
            get: function get() {
              return state.source;
            }
          },

          getMetadata: getMetadata,
          setMetadata: function setMetadata(key, value, silent) {
            if (isObject(key)) {
              var data = key;
              Object.keys(data).forEach(function(key) {
                _setMetadata(key, data[key], value);
              });
              return key;
            }
            _setMetadata(key, value, silent);
            return value;
          },

          extend: function extend(name, handler) {
            return (itemAPI[name] = handler);
          },

          abortLoad: abortLoad,
          retryLoad: retryLoad,
          requestProcessing: requestProcessing,
          abortProcessing: abortProcessing,

          load: load,
          process: process,
          revert: revert
        },
        on(),
        {
          release: function release() {
            return (state.released = true);
          },
          released: {
            get: function get() {
              return state.released;
            }
          },

          archive: function archive() {
            return (state.archived = true);
          },
          archived: {
            get: function get() {
              return state.archived;
            }
          }
        }
      );

      // create it here instead of returning it instantly so we can extend it later
      var itemAPI = createObject(api);

      return itemAPI;
    };

    var getItemIndexByQuery = function getItemIndexByQuery(items, query) {
      // just return first index
      if (isEmpty(query)) {
        return 0;
      }

      // invalid queries
      if (!isString(query)) {
        return -1;
      }

      // return item by id (or -1 if not found)
      return items.findIndex(function(item) {
        return item.id === query;
      });
    };

    var getItemById = function getItemById(items, itemId) {
      var index = getItemIndexByQuery(items, itemId);
      if (index < 0) {
        return;
      }
      return items[index] || null;
    };

    var fetchLocal = function fetchLocal(
      url,
      load,
      error,
      progress,
      abort,
      headers
    ) {
      var request = sendRequest(null, url, {
        method: 'GET',
        responseType: 'blob'
      });

      request.onload = function(xhr) {
        // get headers
        var headers = xhr.getAllResponseHeaders();

        // get filename
        var filename =
          getFileInfoFromHeaders(headers).name || getFilenameFromURL(url);

        // create response
        load(
          createResponse(
            'load',
            xhr.status,
            getFileFromBlob(xhr.response, filename),
            headers
          )
        );
      };

      request.onerror = function(xhr) {
        error(
          createResponse(
            'error',
            xhr.status,
            xhr.statusText,
            xhr.getAllResponseHeaders()
          )
        );
      };

      request.onheaders = function(xhr) {
        headers(
          createResponse('headers', xhr.status, null, xhr.getAllResponseHeaders())
        );
      };

      request.ontimeout = createTimeoutResponse(error);
      request.onprogress = progress;
      request.onabort = abort;

      // should return request
      return request;
    };

    var getDomainFromURL = function getDomainFromURL(url) {
      if (url.indexOf('//') === 0) {
        url = location.protocol + url;
      }
      return url
        .toLowerCase()
        .replace('blob:', '')
        .replace(/([a-z])?:\/\//, '$1')
        .split('/')[0];
    };

    var isExternalURL = function isExternalURL(url) {
      return (
        (url.indexOf(':') > -1 || url.indexOf('//') > -1) &&
        getDomainFromURL(location.href) !== getDomainFromURL(url)
      );
    };

    var isFile = function isFile(value) {
      return value instanceof File || (value instanceof Blob && value.name);
    };

    var dynamicLabel = function dynamicLabel(label) {
      return function() {
        return isFunction(label) ? label.apply(undefined, arguments) : label;
      };
    };

    // TODO:
    // this might cause trouble when multiple filepond instances are running on the same page
    var updateItemsTimeout = null;

    var processingQueue = [];

    var isMockItem = function isMockItem(item) {
      return !isFile(item.file);
    };

    var optionalPromise = function optionalPromise(fn) {
      for (
        var _len = arguments.length,
          params = Array(_len > 1 ? _len - 1 : 0),
          _key = 1;
        _key < _len;
        _key++
      ) {
        params[_key - 1] = arguments[_key];
      }

      return new Promise(function(resolve) {
        if (!fn) {
          return resolve(true);
        }

        var result = fn.apply(undefined, params);

        if (result == null) {
          return resolve(true);
        }

        if (typeof result === 'boolean') {
          return resolve(result);
        }

        if (typeof result.then === 'function') {
          result.then(resolve);
        }
      });
    };

    // returns item based on state
    var getItemByQueryFromState = function getItemByQueryFromState(
      state,
      itemHandler
    ) {
      return function() {
        var _ref =
            arguments.length > 0 && arguments[0] !== undefined
              ? arguments[0]
              : {},
          query = _ref.query,
          _ref$success = _ref.success,
          success = _ref$success === undefined ? function() {} : _ref$success,
          _ref$failure = _ref.failure,
          failure = _ref$failure === undefined ? function() {} : _ref$failure;

        var item = getItemByQuery(state.items, query);
        if (!item) {
          failure({
            error: createResponse('error', 0, 'Item not found'),
            file: null
          });
          return;
        }
        itemHandler(item, success, failure);
      };
    };

    var actions = function actions(dispatch, query, state) {
      return {
        /**
         * Aborts all ongoing processes
         */
        ABORT_ALL: function ABORT_ALL() {
          getActiveItems(state.items).forEach(function(item) {
            item.abortLoad();
            item.abortProcessing();
          });
        },

        /**
         * Sets initial files
         */
        DID_SET_FILES: function DID_SET_FILES(_ref2) {
          var _ref2$value = _ref2.value,
            value = _ref2$value === undefined ? [] : _ref2$value;

          // map values to file objects
          var files = value.map(function(file) {
            return {
              source: file.source ? file.source : file,
              options: file.options
            };
          });

          // loop over files, if file is in list, leave it be, if not, remove

          // test if items should be moved
          var activeItems = getActiveItems(state.items);
          activeItems.forEach(function(item) {
            // if item not is in new value, remove
            if (
              !files.find(function(file) {
                return file.source === item.source || file.source === item.file;
              })
            ) {
              dispatch('REMOVE_ITEM', { query: item });
            }
          });

          // add new files
          activeItems = getActiveItems(state.items);
          files.forEach(function(file, index) {
            // if file is already in list
            if (
              activeItems.find(function(item) {
                return item.source === file.source || item.file === file.source;
              })
            ) {
              return;
            }

            // not in list, add
            dispatch(
              'ADD_ITEM',
              Object.assign({}, file, {
                interactionMethod: InteractionMethod.NONE,
                index: index
              })
            );
          });
        },

        DID_UPDATE_ITEM_METADATA: function DID_UPDATE_ITEM_METADATA(_ref3) {
          var id = _ref3.id;

          var item = getItemById(state.items, id);

          // only revert and attempt to upload when we're uploading to a server
          if (!query('IS_ASYNC')) {
            // should we update the output data
            applyFilterChain('SHOULD_PREPARE_OUTPUT', false, {
              item: item,
              query: query
            }).then(function(shouldPrepareOutput) {
              if (!shouldPrepareOutput) {
                return;
              }
              dispatch(
                'REQUEST_PREPARE_OUTPUT',
                {
                  query: id,
                  item: item,
                  ready: function ready(file) {
                    dispatch('DID_PREPARE_OUTPUT', { id: id, file: file });
                  }
                },
                true
              );
            });

            return;
          }

          // for async scenarios
          var upload = function upload() {
            // we push this forward a bit so the interface is updated correctly
            setTimeout(function() {
              dispatch('REQUEST_ITEM_PROCESSING', { query: id });
            }, 32);
          };

          var revert = function revert(doUpload) {
            item
              .revert(
                createRevertFunction(
                  state.options.server.url,
                  state.options.server.revert
                )
              )
              .then(doUpload ? upload : function() {});
          };

          var abort = function abort(doUpload) {
            item.abortProcessing().then(doUpload ? upload : function() {});
          };

          // if we should re-upload the file immidiately
          if (item.status === ItemStatus.PROCESSING_COMPLETE) {
            return revert(state.options.instantUpload);
          }

          // if currently uploading, cancel upload
          if (item.status === ItemStatus.PROCESSING) {
            return abort(state.options.instantUpload);
          }

          if (state.options.instantUpload) {
            upload();
          }
        },

        /**
         * @param source
         * @param index
         * @param interactionMethod
         */
        ADD_ITEM: function ADD_ITEM(_ref4) {
          var source = _ref4.source,
            index = _ref4.index,
            interactionMethod = _ref4.interactionMethod,
            _ref4$success = _ref4.success,
            success = _ref4$success === undefined ? function() {} : _ref4$success,
            _ref4$failure = _ref4.failure,
            failure = _ref4$failure === undefined ? function() {} : _ref4$failure,
            _ref4$options = _ref4.options,
            options = _ref4$options === undefined ? {} : _ref4$options;

          // if no source supplied
          if (isEmpty(source)) {
            failure({
              error: createResponse('error', 0, 'No source'),
              file: null
            });
            return;
          }

          // filter out invalid file items, used to filter dropped directory contents
          if (
            isFile(source) &&
            state.options.ignoredFiles.includes(source.name.toLowerCase())
          ) {
            // fail silently
            return;
          }

          // test if there's still room in the list of files
          if (!hasRoomForItem(state)) {
            // if multiple allowed, we can't replace
            // or if only a single item is allowed but we're not allowed to replace it we exit
            if (
              state.options.allowMultiple ||
              (!state.options.allowMultiple && !state.options.allowReplace)
            ) {
              var error = createResponse('warning', 0, 'Max files');

              dispatch('DID_THROW_MAX_FILES', {
                source: source,
                error: error
              });

              failure({ error: error, file: null });
              return;
            }

            // let's replace the item
            // id of first item we're about to remove
            var _item = getActiveItems(state.items)[0];

            // if has been processed remove it from the server as well
            if (_item.status === ItemStatus.PROCESSING_COMPLETE) {
              _item.revert(
                createRevertFunction(
                  state.options.server.url,
                  state.options.server.revert
                )
              );
            }

            // remove first item as it will be replaced by this item
            dispatch('REMOVE_ITEM', { query: _item.id });
          }

          // where did the file originate
          var origin =
            options.type === 'local'
              ? FileOrigin$1.LOCAL
              : options.type === 'limbo'
                ? FileOrigin$1.LIMBO
                : FileOrigin$1.INPUT;

          // create a new blank item
          var item = createItem(
            // where did this file come from
            origin,

            // an input file never has a server file reference
            origin === FileOrigin$1.INPUT ? null : source,

            // file mock data, if defined
            options.file
          );

          // set initial meta data
          Object.keys(options.metadata || {}).forEach(function(key) {
            item.setMetadata(key, options.metadata[key]);
          });

          // created the item, let plugins add methods
          applyFilters('DID_CREATE_ITEM', item, { query: query });

          // add item to list
          insertItem(state.items, item, index);

          // get a quick reference to the item id
          var id = item.id;

          // observe item events
          item.on('load-init', function() {
            dispatch('DID_START_ITEM_LOAD', { id: id });
          });

          item.on('load-meta', function() {
            dispatch('DID_UPDATE_ITEM_META', { id: id });
          });

          item.on('load-progress', function(progress) {
            dispatch('DID_UPDATE_ITEM_LOAD_PROGRESS', {
              id: id,
              progress: progress
            });
          });

          item.on('load-request-error', function(error) {
            var mainStatus = dynamicLabel(state.options.labelFileLoadError)(
              error
            );

            // is client error, no way to recover
            if (error.code >= 400 && error.code < 500) {
              dispatch('DID_THROW_ITEM_INVALID', {
                id: id,
                error: error,
                status: {
                  main: mainStatus,
                  sub: error.code + ' (' + error.body + ')'
                }
              });

              // reject the file so can be dealt with through API
              failure({ error: error, file: createItemAPI(item) });
              return;
            }

            // is possible server error, so might be possible to retry
            dispatch('DID_THROW_ITEM_LOAD_ERROR', {
              id: id,
              error: error,
              status: {
                main: mainStatus,
                sub: state.options.labelTapToRetry
              }
            });
          });

          item.on('load-file-error', function(error) {
            dispatch(
              'DID_THROW_ITEM_INVALID',
              Object.assign({}, error, { id: id })
            );
          });

          item.on('load-abort', function() {
            dispatch('REMOVE_ITEM', { query: id });
          });

          item.on('load-skip', function() {
            dispatch('COMPLETE_LOAD_ITEM', {
              query: id,
              item: item,
              data: {
                source: source,
                success: success
              }
            });
          });

          item.on('load', function() {
            var handleAdd = function handleAdd(shouldAdd) {
              // no should not add this file
              if (!shouldAdd) {
                dispatch('REMOVE_ITEM', {
                  query: id
                });
                return;
              }

              // now interested in metadata updates
              item.on('metadata-update', function(change) {
                dispatch('DID_UPDATE_ITEM_METADATA', { id: id, change: change });
              });

              // let plugins decide if the output data should be prepared at this point
              // means we'll do this and wait for idle state
              applyFilterChain('SHOULD_PREPARE_OUTPUT', false, {
                item: item,
                query: query
              }).then(function(shouldPrepareOutput) {
                var loadComplete = function loadComplete() {
                  dispatch('COMPLETE_LOAD_ITEM', {
                    query: id,
                    item: item,
                    data: {
                      source: source,
                      success: success
                    }
                  });
                };

                // exit
                if (shouldPrepareOutput) {
                  // wait for idle state and then run PREPARE_OUTPUT
                  dispatch(
                    'REQUEST_PREPARE_OUTPUT',
                    {
                      query: id,
                      item: item,
                      ready: function ready(file) {
                        dispatch('DID_PREPARE_OUTPUT', { id: id, file: file });
                        loadComplete();
                      }
                    },
                    true
                  );

                  return;
                }

                loadComplete();
              });
            };

            // item loaded, allow plugins to
            // - read data (quickly)
            // - add metadata
            applyFilterChain('DID_LOAD_ITEM', item, {
              query: query,
              dispatch: dispatch
            })
              .then(function() {
                optionalPromise(
                  query('GET_BEFORE_ADD_FILE'),
                  createItemAPI(item)
                ).then(handleAdd);
              })
              .catch(function() {
                handleAdd(false);
              });
          });

          item.on('process-start', function() {
            dispatch('DID_START_ITEM_PROCESSING', { id: id });
          });

          item.on('process-progress', function(progress) {
            dispatch('DID_UPDATE_ITEM_PROCESS_PROGRESS', {
              id: id,
              progress: progress
            });
          });

          item.on('process-error', function(error) {
            dispatch('DID_THROW_ITEM_PROCESSING_ERROR', {
              id: id,
              error: error,
              status: {
                main: dynamicLabel(state.options.labelFileProcessingError)(error),
                sub: state.options.labelTapToRetry
              }
            });
          });

          item.on('process-complete', function(serverFileReference) {
            dispatch('DID_COMPLETE_ITEM_PROCESSING', {
              id: id,
              error: null,
              serverFileReference: serverFileReference
            });
          });

          item.on('process-abort', function() {
            dispatch('DID_ABORT_ITEM_PROCESSING', { id: id });
          });

          item.on('process-revert', function() {
            dispatch('DID_REVERT_ITEM_PROCESSING', { id: id });
          });

          // let view know the item has been inserted
          dispatch('DID_ADD_ITEM', {
            id: id,
            index: index,
            interactionMethod: interactionMethod
          });

          // the item list has been updated
          clearTimeout(updateItemsTimeout);
          updateItemsTimeout = setTimeout(function() {
            dispatch('DID_UPDATE_ITEMS', { items: getActiveItems(state.items) });
          }, 0);

          // start loading the source

          var _ref5 = state.options.server || {},
            url = _ref5.url,
            load = _ref5.load,
            restore = _ref5.restore,
            fetch = _ref5.fetch;

          item.load(
            source,

            // this creates a function that loads the file based on the type of file (string, base64, blob, file) and location of file (local, remote, limbo)
            createFileLoader(
              origin === FileOrigin$1.INPUT
                ? // input
                  isString(source) && isExternalURL(source)
                  ? createFetchFunction(url, fetch) // remote url
                  : fetchLocal // local url
                : // limbo or local
                  origin === FileOrigin$1.LIMBO
                  ? createFetchFunction(url, restore) // limbo
                  : createFetchFunction(url, load) // local
            ),

            // called when the file is loaded so it can be piped through the filters
            function(file, success, error) {
              // let's process the file
              applyFilterChain('LOAD_FILE', file, { query: query })
                .then(success)
                .catch(error);
            }
          );
        },

        REQUEST_PREPARE_OUTPUT: function REQUEST_PREPARE_OUTPUT(_ref6) {
          var item = _ref6.item,
            ready = _ref6.ready;

          // allow plugins to alter the file data
          applyFilterChain('PREPARE_OUTPUT', item.file, {
            query: query,
            item: item
          }).then(function(result) {
            applyFilterChain('COMPLETE_PREPARE_OUTPUT', result, {
              query: query,
              item: item
            }).then(function(result) {
              ready(result);
            });
          });
        },

        COMPLETE_LOAD_ITEM: function COMPLETE_LOAD_ITEM(_ref7) {
          var item = _ref7.item,
            data = _ref7.data;
          var success = data.success,
            source = data.source;

          // let interface know the item has loaded

          dispatch('DID_LOAD_ITEM', {
            id: item.id,
            error: null,
            serverFileReference:
              item.origin === FileOrigin$1.INPUT ? null : source
          });

          // item has been successfully loaded and added to the
          // list of items so can now be safely returned for use
          success(createItemAPI(item));

          // if this is a local server file we need to show a different state
          if (item.origin === FileOrigin$1.LOCAL) {
            dispatch('DID_LOAD_LOCAL_ITEM', { id: item.id });
            return;
          }

          // if is a temp server file we prevent async upload call here (as the file is already on the server)
          if (item.origin === FileOrigin$1.LIMBO) {
            dispatch('DID_COMPLETE_ITEM_PROCESSING', {
              id: item.id,
              error: null,
              serverFileReference: source
            });
            return;
          }

          // id we are allowed to upload the file immidiately, lets do it
          if (query('IS_ASYNC') && state.options.instantUpload) {
            dispatch('REQUEST_ITEM_PROCESSING', { query: item.id });
          }
        },

        RETRY_ITEM_LOAD: getItemByQueryFromState(state, function(item) {
          // try loading the source one more time
          item.retryLoad();
        }),

        REQUEST_ITEM_PROCESSING: getItemByQueryFromState(state, function(
          item,
          success,
          failure
        ) {
          // cannot be queued (or is already queued)
          var itemCanBeQueuedForProcessing =
            // waiting for something
            item.status === ItemStatus.IDLE ||
            // processing went wrong earlier
            item.status === ItemStatus.PROCESSING_ERROR ||
            // was paused
            item.status === ItemStatus.PROCESSING_PAUSED;

          // not ready to be processed
          if (!itemCanBeQueuedForProcessing) {
            var process = function process() {
              setTimeout(function() {
                dispatch('REQUEST_ITEM_PROCESSING', {
                  query: item,
                  success: success,
                  failure: failure
                });
              }, 32);
            };

            if (item.status === ItemStatus.PROCESSING_COMPLETE) {
              item
                .revert(
                  createRevertFunction(
                    state.options.server.url,
                    state.options.server.revert
                  )
                )
                .then(process);
            } else if (item.status === ItemStatus.PROCESSING) {
              item.abortProcessing().then(process);
            }

            return;
          }

          // already queued for processing
          if (item.status === ItemStatus.PROCESSING_QUEUED) return;

          item.requestProcessing();

          dispatch('DID_REQUEST_ITEM_PROCESSING', { id: item.id });

          dispatch(
            'PROCESS_ITEM',
            { query: item, success: success, failure: failure },
            true
          );
        }),

        PROCESS_ITEM: getItemByQueryFromState(state, function(
          item,
          success,
          failure
        ) {
          var maxParallelUploads = query('GET_MAX_PARALLEL_UPLOADS');
          var totalCurrentUploads = query(
            'GET_ITEMS_BY_STATUS',
            ItemStatus.PROCESSING
          ).length;

          // queue and wait till queue is freed up
          if (totalCurrentUploads === maxParallelUploads) {
            // queue for later processing
            processingQueue.push({
              item: item,
              success: success,
              failure: failure
            });

            // stop it!
            return;
          }

          // if was not queued or is already processing exit here
          if (item.status === ItemStatus.PROCESSING) return;

          // we done function
          item.onOnce('process-complete', function() {
            // done!
            success(createItemAPI(item));

            // process queueud items
            var queued = processingQueue.shift();
            if (!queued) return;
            dispatch(
              'PROCESS_ITEM',
              {
                query: queued.item,
                success: queued.success,
                failure: queued.failure
              },
              true
            );
          });

          // we error function
          item.onOnce('process-error', function(error) {
            failure({ error: error, file: createItemAPI(item) });
          });

          // start file processing
          item.process(
            createFileProcessor(
              createProcessorFunction(
                state.options.server.url,
                state.options.server.process,
                state.options.name
              )
            ),
            // called when the file is about to be processed so it can be piped through the transform filters
            function(file, success, error) {
              // allow plugins to alter the file data
              applyFilterChain('PREPARE_OUTPUT', file, {
                query: query,
                item: item
              })
                .then(function(file) {
                  dispatch('DID_PREPARE_OUTPUT', { id: item.id, file: file });
                  success(file);
                })
                .catch(error);
            }
          );
        }),

        RETRY_ITEM_PROCESSING: getItemByQueryFromState(state, function(item) {
          dispatch('REQUEST_ITEM_PROCESSING', { query: item });
        }),

        REQUEST_REMOVE_ITEM: getItemByQueryFromState(state, function(item) {
          optionalPromise(
            query('GET_BEFORE_REMOVE_FILE'),
            createItemAPI(item)
          ).then(function(shouldRemove) {
            if (!shouldRemove) {
              return;
            }
            dispatch('REMOVE_ITEM', { query: item });
          });
        }),

        RELEASE_ITEM: getItemByQueryFromState(state, function(item) {
          item.release();
        }),

        REMOVE_ITEM: getItemByQueryFromState(state, function(item, success) {
          var removeFromView = function removeFromView() {
            // get id reference
            var id = item.id;

            // archive the item, this does not remove it from the list
            getItemById(state.items, id).archive();

            // tell the view the item has been removed
            dispatch('DID_REMOVE_ITEM', { id: id, item: item });

            // now the list has been modified
            clearTimeout(updateItemsTimeout);
            updateItemsTimeout = setTimeout(function() {
              dispatch('DID_UPDATE_ITEMS', {
                items: getActiveItems(state.items)
              });
            }, 0);

            // correctly removed
            success(createItemAPI(item));
          };

          // if this is a local file and the server.remove function has been configured, send source there so dev can remove file from server
          var server = state.options.server;
          if (
            item.origin === FileOrigin$1.LOCAL &&
            server &&
            isFunction(server.remove)
          ) {
            dispatch('DID_START_ITEM_REMOVE', { id: item.id });

            server.remove(
              item.source,
              function() {
                return removeFromView();
              },
              function(status) {
                dispatch('DID_THROW_ITEM_REMOVE_ERROR', {
                  id: item.id,
                  error: createResponse('error', 0, status, null),
                  status: {
                    main: status,
                    sub: state.options.labelTapToRetry
                  }
                });
              }
            );
          } else {
            removeFromView();
          }
        }),

        ABORT_ITEM_LOAD: getItemByQueryFromState(state, function(item) {
          item.abortLoad();
        }),

        ABORT_ITEM_PROCESSING: getItemByQueryFromState(state, function(item) {
          // test if is already processed
          if (item.serverId) {
            dispatch('REVERT_ITEM_PROCESSING', { id: item.id });
            return;
          }

          // abort
          item.abortProcessing().then(function() {
            var shouldRemove = state.options.instantUpload;
            if (shouldRemove) {
              dispatch('REMOVE_ITEM', { query: item.id });
            }
          });
        }),

        REQUEST_REVERT_ITEM_PROCESSING: getItemByQueryFromState(state, function(
          item
        ) {
          // not instant uploading, revert immidiately
          if (!state.options.instantUpload) {
            dispatch('REVERT_ITEM_PROCESSING', { query: item });
            return;
          }

          // if we're instant uploading the file will also be removed if we revert,
          // so if a before remove file hook is defined we need to run it now
          var handleRevert = function handleRevert(shouldRevert) {
            if (!shouldRevert) return;
            dispatch('REVERT_ITEM_PROCESSING', { query: item });
          };

          var fn = query('GET_BEFORE_REMOVE_FILE');
          if (!fn) {
            return handleRevert(true);
          }

          var requestRemoveResult = fn(createItemAPI(item));
          if (requestRemoveResult == null) {
            // undefined or null
            return handleRevert(true);
          }

          if (typeof requestRemoveResult === 'boolean') {
            return handleRevert(requestRemoveResult);
          }

          if (typeof requestRemoveResult.then === 'function') {
            requestRemoveResult.then(handleRevert);
          }
        }),

        REVERT_ITEM_PROCESSING: getItemByQueryFromState(state, function(item) {
          item
            .revert(
              createRevertFunction(
                state.options.server.url,
                state.options.server.revert
              )
            )
            .then(function() {
              var shouldRemove = state.options.instantUpload || isMockItem(item);
              if (shouldRemove) {
                dispatch('REMOVE_ITEM', { query: item.id });
              }
            });
        }),

        SET_OPTIONS: function SET_OPTIONS(_ref8) {
          var options = _ref8.options;

          forin(options, function(key, value) {
            dispatch('SET_' + fromCamels(key, '_').toUpperCase(), {
              value: value
            });
          });
        }
      };
    };

    var formatFilename = function formatFilename(name) {
      return name;
    };

    var createElement$1 = function createElement(tagName) {
      return document.createElement(tagName);
    };

    var text = function text(node, value) {
      var textNode = node.childNodes[0];
      if (!textNode) {
        textNode = document.createTextNode(value);
        node.appendChild(textNode);
      } else if (value !== textNode.nodeValue) {
        textNode.nodeValue = value;
      }
    };

    var polarToCartesian = function polarToCartesian(
      centerX,
      centerY,
      radius,
      angleInDegrees
    ) {
      var angleInRadians = (angleInDegrees % 360 - 90) * Math.PI / 180.0;
      return {
        x: centerX + radius * Math.cos(angleInRadians),
        y: centerY + radius * Math.sin(angleInRadians)
      };
    };

    var describeArc = function describeArc(
      x,
      y,
      radius,
      startAngle,
      endAngle,
      arcSweep
    ) {
      var start = polarToCartesian(x, y, radius, endAngle);
      var end = polarToCartesian(x, y, radius, startAngle);
      return [
        'M',
        start.x,
        start.y,
        'A',
        radius,
        radius,
        0,
        arcSweep,
        0,
        end.x,
        end.y
      ].join(' ');
    };

    var percentageArc = function percentageArc(x, y, radius, from, to) {
      var arcSweep = 1;
      if (to > from && to - from <= 0.5) {
        arcSweep = 0;
      }
      if (from > to && from - to >= 0.5) {
        arcSweep = 0;
      }
      return describeArc(
        x,
        y,
        radius,
        Math.min(0.9999, from) * 360,
        Math.min(0.9999, to) * 360,
        arcSweep
      );
    };

    var create$7 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // start at 0
      props.spin = false;
      props.progress = 0;
      props.opacity = 0;

      // svg
      var svg = createElement('svg');
      root.ref.path = createElement('path', {
        'stroke-width': 2,
        'stroke-linecap': 'round'
      });
      svg.appendChild(root.ref.path);

      root.ref.svg = svg;

      root.appendChild(svg);
    };

    var write$5 = function write(_ref2) {
      var root = _ref2.root,
        props = _ref2.props;

      if (props.opacity === 0) {
        return;
      }

      if (props.align) {
        root.element.dataset.align = props.align;
      }

      // get width of stroke
      var ringStrokeWidth = parseInt(attr(root.ref.path, 'stroke-width'), 10);

      // calculate size of ring
      var size = root.rect.element.width * 0.5;

      // ring state
      var ringFrom = 0;
      var ringTo = 0;

      // now in busy mode
      if (props.spin) {
        ringFrom = 0;
        ringTo = 0.5;
      } else {
        ringFrom = 0;
        ringTo = props.progress;
      }

      // get arc path
      var coordinates = percentageArc(
        size,
        size,
        size - ringStrokeWidth,
        ringFrom,
        ringTo
      );

      // update progress bar
      attr(root.ref.path, 'd', coordinates);

      // hide while contains 0 value
      attr(
        root.ref.path,
        'stroke-opacity',
        props.spin || props.progress > 0 ? 1 : 0
      );
    };

    var progressIndicator = createView({
      tag: 'div',
      name: 'progress-indicator',
      ignoreRectUpdate: true,
      ignoreRect: true,
      create: create$7,
      write: write$5,
      mixins: {
        apis: ['progress', 'spin', 'align'],
        styles: ['opacity'],
        animations: {
          opacity: { type: 'tween', duration: 500 },
          progress: {
            type: 'spring',
            stiffness: 0.95,
            damping: 0.65,
            mass: 10
          }
        }
      }
    });

    var create$8 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      root.element.title = props.label;
      root.element.innerHTML = props.icon || '';
      props.disabled = false;
    };

    var write$6 = function write(_ref2) {
      var root = _ref2.root,
        props = _ref2.props;

      if (props.opacity === 0 && !props.disabled) {
        props.disabled = true;
        attr(root.element, 'disabled', 'disabled');
      } else if (props.opacity > 0 && props.disabled) {
        props.disabled = false;
        root.element.removeAttribute('disabled');
      }
    };

    var fileActionButton = createView({
      tag: 'button',
      attributes: {
        type: 'button'
      },
      ignoreRect: true,
      ignoreRectUpdate: true,
      name: 'file-action-button',
      mixins: {
        apis: ['label'],
        styles: ['translateX', 'translateY', 'scaleX', 'scaleY', 'opacity'],
        animations: {
          scaleX: 'spring',
          scaleY: 'spring',
          translateX: 'spring',
          translateY: 'spring',
          opacity: { type: 'tween', duration: 250 }
        },
        listeners: true
      },
      create: create$8,
      write: write$6
    });

    var toNaturalFileSize = function toNaturalFileSize(bytes) {
      var decimalSeparator =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '.';

      // nope, no negative byte sizes
      bytes = Math.round(Math.abs(bytes));

      // just bytes
      if (bytes < 1000) {
        return bytes + ' bytes';
      }

      // kilobytes
      if (bytes < MB) {
        return Math.floor(bytes / KB) + ' KB';
      }

      // megabytes
      if (bytes < GB) {
        return removeDecimalsWhenZero(bytes / MB, 1, decimalSeparator) + ' MB';
      }

      // gigabytes
      return removeDecimalsWhenZero(bytes / GB, 2, decimalSeparator) + ' GB';
    };

    var KB = 1000;
    var MB = 1000000;
    var GB = 1000000000;

    var removeDecimalsWhenZero = function removeDecimalsWhenZero(
      value,
      decimalCount,
      separator
    ) {
      return value
        .toFixed(decimalCount)
        .split('.')
        .filter(function(part) {
          return part !== '0';
        })
        .join(separator);
    };

    var create$9 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // filename
      var fileName = createElement$1('span');
      fileName.className = 'filepond--file-info-main';
      // hide for screenreaders
      // the file is contained in a fieldset with legend that contains the filename
      // no need to read it twice
      attr(fileName, 'aria-hidden', 'true');
      root.appendChild(fileName);
      root.ref.fileName = fileName;

      // filesize
      var fileSize = createElement$1('span');
      fileSize.className = 'filepond--file-info-sub';
      root.appendChild(fileSize);
      root.ref.fileSize = fileSize;

      // set initial values
      text(fileSize, root.query('GET_LABEL_FILE_WAITING_FOR_SIZE'));
      text(fileName, formatFilename(root.query('GET_ITEM_NAME', props.id)));
    };

    var updateFile = function updateFile(_ref2) {
      var root = _ref2.root,
        props = _ref2.props;

      text(
        root.ref.fileSize,
        toNaturalFileSize(root.query('GET_ITEM_SIZE', props.id))
      );
      text(
        root.ref.fileName,
        formatFilename(root.query('GET_ITEM_NAME', props.id))
      );
    };

    var updateFileSizeOnError = function updateFileSizeOnError(_ref3) {
      var root = _ref3.root,
        props = _ref3.props;

      // if size is available don't fallback to unknown size message
      if (isInt(root.query('GET_ITEM_SIZE', props.id))) {
        return;
      }

      text(root.ref.fileSize, root.query('GET_LABEL_FILE_SIZE_NOT_AVAILABLE'));
    };

    var fileInfo = createView({
      name: 'file-info',
      ignoreRect: true,
      ignoreRectUpdate: true,
      write: createRoute({
        DID_LOAD_ITEM: updateFile,
        DID_UPDATE_ITEM_META: updateFile,
        DID_THROW_ITEM_LOAD_ERROR: updateFileSizeOnError,
        DID_THROW_ITEM_INVALID: updateFileSizeOnError
      }),
      didCreateView: function didCreateView(root) {
        applyFilters('CREATE_VIEW', Object.assign({}, root, { view: root }));
      },
      create: create$9,
      mixins: {
        styles: ['translateX', 'translateY'],
        animations: {
          translateX: 'spring',
          translateY: 'spring'
        }
      }
    });

    var toPercentage = function toPercentage(value) {
      return Math.round(value * 100);
    };

    var create$10 = function create(_ref) {
      var root = _ref.root;

      // main status
      var main = createElement$1('span');
      main.className = 'filepond--file-status-main';
      root.appendChild(main);
      root.ref.main = main;

      // sub status
      var sub = createElement$1('span');
      sub.className = 'filepond--file-status-sub';
      root.appendChild(sub);
      root.ref.sub = sub;

      didSetItemLoadProgress({ root: root, action: { progress: null } });
    };

    var didSetItemLoadProgress = function didSetItemLoadProgress(_ref2) {
      var root = _ref2.root,
        action = _ref2.action;

      var title =
        action.progress === null
          ? root.query('GET_LABEL_FILE_LOADING')
          : root.query('GET_LABEL_FILE_LOADING') +
            ' ' +
            toPercentage(action.progress) +
            '%';
      text(root.ref.main, title);
      text(root.ref.sub, root.query('GET_LABEL_TAP_TO_CANCEL'));
    };

    var didSetItemProcessProgress = function didSetItemProcessProgress(_ref3) {
      var root = _ref3.root,
        action = _ref3.action;

      var title =
        action.progress === null
          ? root.query('GET_LABEL_FILE_PROCESSING')
          : root.query('GET_LABEL_FILE_PROCESSING') +
            ' ' +
            toPercentage(action.progress) +
            '%';
      text(root.ref.main, title);
      text(root.ref.sub, root.query('GET_LABEL_TAP_TO_CANCEL'));
    };

    var didRequestItemProcessing = function didRequestItemProcessing(_ref4) {
      var root = _ref4.root;

      text(root.ref.main, root.query('GET_LABEL_FILE_PROCESSING'));
      text(root.ref.sub, root.query('GET_LABEL_TAP_TO_CANCEL'));
    };

    var didAbortItemProcessing = function didAbortItemProcessing(_ref5) {
      var root = _ref5.root;

      text(root.ref.main, root.query('GET_LABEL_FILE_PROCESSING_ABORTED'));
      text(root.ref.sub, root.query('GET_LABEL_TAP_TO_RETRY'));
    };

    var didCompleteItemProcessing$1 = function didCompleteItemProcessing(_ref6) {
      var root = _ref6.root;

      text(root.ref.main, root.query('GET_LABEL_FILE_PROCESSING_COMPLETE'));
      text(root.ref.sub, root.query('GET_LABEL_TAP_TO_UNDO'));
    };

    var clear = function clear(_ref7) {
      var root = _ref7.root;

      text(root.ref.main, '');
      text(root.ref.sub, '');
    };

    var error = function error(_ref8) {
      var root = _ref8.root,
        action = _ref8.action;

      text(root.ref.main, action.status.main);
      text(root.ref.sub, action.status.sub);
    };

    var fileStatus = createView({
      name: 'file-status',
      ignoreRect: true,
      ignoreRectUpdate: true,
      write: createRoute({
        DID_LOAD_ITEM: clear,
        DID_REVERT_ITEM_PROCESSING: clear,
        DID_REQUEST_ITEM_PROCESSING: didRequestItemProcessing,
        DID_ABORT_ITEM_PROCESSING: didAbortItemProcessing,
        DID_COMPLETE_ITEM_PROCESSING: didCompleteItemProcessing$1,
        DID_UPDATE_ITEM_PROCESS_PROGRESS: didSetItemProcessProgress,
        DID_UPDATE_ITEM_LOAD_PROGRESS: didSetItemLoadProgress,
        DID_THROW_ITEM_LOAD_ERROR: error,
        DID_THROW_ITEM_INVALID: error,
        DID_THROW_ITEM_PROCESSING_ERROR: error,
        DID_THROW_ITEM_REMOVE_ERROR: error
      }),
      didCreateView: function didCreateView(root) {
        applyFilters('CREATE_VIEW', Object.assign({}, root, { view: root }));
      },
      create: create$10,
      mixins: {
        styles: ['translateX', 'translateY', 'opacity'],
        animations: {
          opacity: { type: 'tween', duration: 250 },
          translateX: 'spring',
          translateY: 'spring'
        }
      }
    });

    /**
     * Button definitions for the file view
     */

    var Buttons = {
      AbortItemLoad: {
        label: 'GET_LABEL_BUTTON_ABORT_ITEM_LOAD',
        action: 'ABORT_ITEM_LOAD',
        className: 'filepond--action-abort-item-load',
        align: 'LOAD_INDICATOR_POSITION' // right
      },
      RetryItemLoad: {
        label: 'GET_LABEL_BUTTON_RETRY_ITEM_LOAD',
        action: 'RETRY_ITEM_LOAD',
        icon: 'GET_ICON_RETRY',
        className: 'filepond--action-retry-item-load',
        align: 'BUTTON_PROCESS_ITEM_POSITION' // right
      },
      RemoveItem: {
        label: 'GET_LABEL_BUTTON_REMOVE_ITEM',
        action: 'REQUEST_REMOVE_ITEM',
        icon: 'GET_ICON_REMOVE',
        className: 'filepond--action-remove-item',
        align: 'BUTTON_REMOVE_ITEM_POSITION' // left
      },
      ProcessItem: {
        label: 'GET_LABEL_BUTTON_PROCESS_ITEM',
        action: 'REQUEST_ITEM_PROCESSING',
        icon: 'GET_ICON_PROCESS',
        className: 'filepond--action-process-item',
        align: 'BUTTON_PROCESS_ITEM_POSITION' // right
      },
      AbortItemProcessing: {
        label: 'GET_LABEL_BUTTON_ABORT_ITEM_PROCESSING',
        action: 'ABORT_ITEM_PROCESSING',
        className: 'filepond--action-abort-item-processing',
        align: 'BUTTON_PROCESS_ITEM_POSITION' // right
      },
      RetryItemProcessing: {
        label: 'GET_LABEL_BUTTON_RETRY_ITEM_PROCESSING',
        action: 'RETRY_ITEM_PROCESSING',
        icon: 'GET_ICON_RETRY',
        className: 'filepond--action-retry-item-processing',
        align: 'BUTTON_PROCESS_ITEM_POSITION' // right
      },
      RevertItemProcessing: {
        label: 'GET_LABEL_BUTTON_UNDO_ITEM_PROCESSING',
        action: 'REQUEST_REVERT_ITEM_PROCESSING',
        icon: 'GET_ICON_UNDO',
        className: 'filepond--action-revert-item-processing',
        align: 'BUTTON_PROCESS_ITEM_POSITION' // right
      }
    };

    // make a list of buttons, we can then remove buttons from this list if they're disabled
    var ButtonKeys = [];
    forin(Buttons, function(key) {
      ButtonKeys.push(key);
    });

    var calculateFileInfoOffset = function calculateFileInfoOffset(root) {
      return (
        root.ref.buttonRemoveItem.rect.element.width +
        root.ref.buttonRemoveItem.rect.element.left
      );
    };

    // Force on full pixels so text stays crips
    var calculateFileVerticalCenterOffset = function calculateFileVerticalCenterOffset(
      root
    ) {
      return Math.floor(root.ref.buttonRemoveItem.rect.element.height / 4);
    };
    var calculateFileHorizontalCenterOffset = function calculateFileHorizontalCenterOffset(
      root
    ) {
      return Math.floor(root.ref.buttonRemoveItem.rect.element.left / 2);
    };

    var getLoadIndicatorAlignment = function getLoadIndicatorAlignment(root) {
      return root.query('GET_STYLE_LOAD_INDICATOR_POSITION');
    };
    var getProcessIndicatorAlignment = function getProcessIndicatorAlignment(
      root
    ) {
      return root.query('GET_STYLE_PROGRESS_INDICATOR_POSITION');
    };
    var getRemoveIndicatorAligment = function getRemoveIndicatorAligment(root) {
      return root.query('GET_STYLE_BUTTON_REMOVE_ITEM_POSITION');
    };

    var DefaultStyle = {
      buttonAbortItemLoad: { opacity: 0 },
      buttonRetryItemLoad: { opacity: 0 },
      buttonRemoveItem: { opacity: 0 },
      buttonProcessItem: { opacity: 0 },
      buttonAbortItemProcessing: { opacity: 0 },
      buttonRetryItemProcessing: { opacity: 0 },
      buttonRevertItemProcessing: { opacity: 0 },
      loadProgressIndicator: { opacity: 0, align: getLoadIndicatorAlignment },
      processProgressIndicator: {
        opacity: 0,
        align: getProcessIndicatorAlignment
      },
      processingCompleteIndicator: { opacity: 0, scaleX: 0.75, scaleY: 0.75 },
      info: { translateX: 0, translateY: 0, opacity: 0 },
      status: { translateX: 0, translateY: 0, opacity: 0 }
    };

    var IdleStyle = {
      buttonRemoveItem: { opacity: 1 },
      buttonProcessItem: { opacity: 1 },
      info: { translateX: calculateFileInfoOffset },
      status: { translateX: calculateFileInfoOffset }
    };

    var ProcessingStyle = {
      buttonAbortItemProcessing: { opacity: 1 },
      processProgressIndicator: { opacity: 1 },
      status: { opacity: 1 }
    };

    var StyleMap = {
      DID_THROW_ITEM_INVALID: {
        buttonRemoveItem: { opacity: 1 },
        info: { translateX: calculateFileInfoOffset },
        status: { translateX: calculateFileInfoOffset, opacity: 1 }
      },

      DID_START_ITEM_LOAD: {
        buttonAbortItemLoad: { opacity: 1 },
        loadProgressIndicator: { opacity: 1 },
        status: { opacity: 1 }
      },
      DID_THROW_ITEM_LOAD_ERROR: {
        buttonRetryItemLoad: { opacity: 1 },
        buttonRemoveItem: { opacity: 1 },
        info: { translateX: calculateFileInfoOffset },
        status: { opacity: 1 }
      },

      DID_START_ITEM_REMOVE: {
        processProgressIndicator: {
          opacity: 1,
          align: getRemoveIndicatorAligment
        },
        info: { translateX: calculateFileInfoOffset },
        status: { opacity: 0 }
      },

      DID_THROW_ITEM_REMOVE_ERROR: {
        processProgressIndicator: {
          opacity: 0,
          align: getRemoveIndicatorAligment
        },
        buttonRemoveItem: { opacity: 1 },
        info: { translateX: calculateFileInfoOffset },
        status: { opacity: 1, translateX: calculateFileInfoOffset }
      },

      DID_LOAD_ITEM: IdleStyle,
      DID_LOAD_LOCAL_ITEM: {
        buttonRemoveItem: { opacity: 1 },
        info: { translateX: calculateFileInfoOffset },
        status: { translateX: calculateFileInfoOffset }
      },
      DID_START_ITEM_PROCESSING: ProcessingStyle,
      DID_REQUEST_ITEM_PROCESSING: ProcessingStyle,
      DID_UPDATE_ITEM_PROCESS_PROGRESS: ProcessingStyle,
      DID_COMPLETE_ITEM_PROCESSING: {
        buttonRevertItemProcessing: { opacity: 1 },
        info: { opacity: 1 },
        status: { opacity: 1 }
      },
      DID_THROW_ITEM_PROCESSING_ERROR: {
        buttonRemoveItem: { opacity: 1 },
        buttonRetryItemProcessing: { opacity: 1 },
        status: { opacity: 1 },
        info: { translateX: calculateFileInfoOffset }
      },
      DID_ABORT_ITEM_PROCESSING: {
        buttonRemoveItem: { opacity: 1 },
        buttonProcessItem: { opacity: 1 },
        info: { translateX: calculateFileInfoOffset },
        status: { opacity: 1 }
      },
      DID_REVERT_ITEM_PROCESSING: IdleStyle
    };

    // complete indicator view
    var processingCompleteIndicatorView = createView({
      create: function create(_ref) {
        var root = _ref.root;

        root.element.innerHTML = root.query('GET_ICON_DONE');
      },
      name: 'processing-complete-indicator',
      ignoreRect: true,
      mixins: {
        styles: ['scaleX', 'scaleY', 'opacity'],
        animations: {
          scaleX: 'spring',
          scaleY: 'spring',
          opacity: { type: 'tween', duration: 250 }
        }
      }
    });

    /**
     * Creates the file view
     */
    var create$6 = function create(_ref2) {
      var root = _ref2.root,
        props = _ref2.props;
      var id = props.id;

      // allow reverting upload

      var allowRevert = root.query('GET_ALLOW_REVERT');

      // is instant uploading, need this to determine the icon of the undo button
      var instantUpload = root.query('GET_INSTANT_UPLOAD');

      // is async set up
      var isAsync = root.query('IS_ASYNC');

      // enabled buttons array
      var enabledButtons = isAsync
        ? ButtonKeys.concat()
        : ButtonKeys.filter(function(key) {
            return !/Process/.test(key);
          });

      // remove last button (revert) if not allowed
      if (isAsync && !allowRevert) {
        enabledButtons.splice(-1, 1);
        var map = StyleMap['DID_COMPLETE_ITEM_PROCESSING'];
        map.info.translateX = calculateFileHorizontalCenterOffset;
        map.info.translateY = calculateFileVerticalCenterOffset;
        map.status.translateY = calculateFileVerticalCenterOffset;
        map.processingCompleteIndicator = { opacity: 1, scaleX: 1, scaleY: 1 };
      }

      // update icon and label for revert button when instant uploading
      if (instantUpload && allowRevert) {
        Buttons['RevertItemProcessing'].label = 'GET_LABEL_BUTTON_REMOVE_ITEM';
        Buttons['RevertItemProcessing'].icon = 'GET_ICON_REMOVE';
      }

      // create the button views
      forin(Buttons, function(key, definition) {
        // create button
        var buttonView = root.createChildView(fileActionButton, {
          label: root.query(definition.label),
          icon: root.query(definition.icon),
          opacity: 0
        });

        // should be appended?
        if (enabledButtons.includes(key)) {
          root.appendChildView(buttonView);
        }

        // add position attribute
        buttonView.element.dataset.align = root.query(
          'GET_STYLE_' + definition.align
        );

        // add class
        buttonView.element.classList.add(definition.className);

        // handle interactions
        buttonView.on('click', function() {
          root.dispatch(definition.action, { query: id });
        });

        // set reference
        root.ref['button' + key] = buttonView;
      });

      // create file info view
      root.ref.info = root.appendChildView(
        root.createChildView(fileInfo, { id: id })
      );

      // create file status view
      root.ref.status = root.appendChildView(
        root.createChildView(fileStatus, { id: id })
      );

      // checkmark
      root.ref.processingCompleteIndicator = root.appendChildView(
        root.createChildView(processingCompleteIndicatorView)
      );
      root.ref.processingCompleteIndicator.element.dataset.align = root.query(
        'GET_STYLE_BUTTON_PROCESS_ITEM_POSITION'
      );

      // add progress indicators
      var loadIndicatorView = root.appendChildView(
        root.createChildView(progressIndicator, {
          opacity: 0,
          align: root.query('GET_STYLE_LOAD_INDICATOR_POSITION')
        })
      );
      loadIndicatorView.element.classList.add('filepond--load-indicator');
      root.ref.loadProgressIndicator = loadIndicatorView;

      var progressIndicatorView = root.appendChildView(
        root.createChildView(progressIndicator, {
          opacity: 0,
          align: root.query('GET_STYLE_PROGRESS_INDICATOR_POSITION')
        })
      );
      progressIndicatorView.element.classList.add('filepond--process-indicator');
      root.ref.processProgressIndicator = progressIndicatorView;
    };

    var write$4 = function write(_ref3) {
      var root = _ref3.root,
        actions = _ref3.actions,
        props = _ref3.props;

      // route actions
      route$3({ root: root, actions: actions, props: props });

      // select last state change action
      var action = []
        .concat(toConsumableArray(actions))
        .filter(function(action) {
          return /^DID_/.test(action.type);
        })
        .reverse()
        .find(function(action) {
          return StyleMap[action.type];
        });

      // no need to set same state twice
      if (!action || (action && action.type === root.ref.currentAction)) {
        return;
      }

      // set current state
      root.ref.currentAction = action.type;
      var newStyles = StyleMap[root.ref.currentAction];

      forin(DefaultStyle, function(name, defaultStyles) {
        // get reference to control
        var control = root.ref[name];

        // loop over all styles for this control
        forin(defaultStyles, function(key, defaultValue) {
          var value =
            newStyles[name] && typeof newStyles[name][key] !== 'undefined'
              ? newStyles[name][key]
              : defaultValue;
          control[key] = typeof value === 'function' ? value(root) : value;
        });
      });
    };

    var route$3 = createRoute({
      DID_SET_LABEL_BUTTON_ABORT_ITEM_PROCESSING: function DID_SET_LABEL_BUTTON_ABORT_ITEM_PROCESSING(
        _ref4
      ) {
        var root = _ref4.root,
          action = _ref4.action;

        root.ref.buttonAbortItemProcessing.label = action.value;
      },
      DID_SET_LABEL_BUTTON_ABORT_ITEM_LOAD: function DID_SET_LABEL_BUTTON_ABORT_ITEM_LOAD(
        _ref5
      ) {
        var root = _ref5.root,
          action = _ref5.action;

        root.ref.buttonAbortItemLoad.label = action.value;
      },
      DID_SET_LABEL_BUTTON_ABORT_ITEM_REMOVAL: function DID_SET_LABEL_BUTTON_ABORT_ITEM_REMOVAL(
        _ref6
      ) {
        var root = _ref6.root,
          action = _ref6.action;

        root.ref.buttonAbortItemRemoval.label = action.value;
      },
      DID_REQUEST_ITEM_PROCESSING: function DID_REQUEST_ITEM_PROCESSING(_ref7) {
        var root = _ref7.root;

        root.ref.processProgressIndicator.spin = true;
        root.ref.processProgressIndicator.progress = 0;
      },
      DID_START_ITEM_LOAD: function DID_START_ITEM_LOAD(_ref8) {
        var root = _ref8.root;

        root.ref.loadProgressIndicator.spin = true;
        root.ref.loadProgressIndicator.progress = 0;
      },
      DID_START_ITEM_REMOVE: function DID_START_ITEM_REMOVE(_ref9) {
        var root = _ref9.root;

        root.ref.processProgressIndicator.spin = true;
        root.ref.processProgressIndicator.progress = 0;
      },
      DID_UPDATE_ITEM_LOAD_PROGRESS: function DID_UPDATE_ITEM_LOAD_PROGRESS(
        _ref10
      ) {
        var root = _ref10.root,
          action = _ref10.action;

        root.ref.loadProgressIndicator.spin = false;
        root.ref.loadProgressIndicator.progress = action.progress;
      },
      DID_UPDATE_ITEM_PROCESS_PROGRESS: function DID_UPDATE_ITEM_PROCESS_PROGRESS(
        _ref11
      ) {
        var root = _ref11.root,
          action = _ref11.action;

        root.ref.processProgressIndicator.spin = false;
        root.ref.processProgressIndicator.progress = action.progress;
      }
    });

    var file = createView({
      create: create$6,
      write: write$4,
      didCreateView: function didCreateView(root) {
        applyFilters('CREATE_VIEW', Object.assign({}, root, { view: root }));
      },
      name: 'file'
    });

    /**
     * Creates the file view
     */
    var create$5 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // filename
      root.ref.fileName = createElement$1('legend');
      root.appendChild(root.ref.fileName);

      // file view
      root.ref.file = root.appendChildView(
        root.createChildView(file, { id: props.id })
      );

      // create data container
      var dataContainer = createElement$1('input');
      dataContainer.type = 'hidden';
      dataContainer.name = root.query('GET_NAME');
      root.ref.data = dataContainer;
      root.appendChild(dataContainer);
    };

    /**
     * Data storage
     */
    var didLoadItem = function didLoadItem(_ref2) {
      var root = _ref2.root,
        action = _ref2.action,
        props = _ref2.props;

      root.ref.data.value = action.serverFileReference;

      // updates the legend of the fieldset so screenreaders can better group buttons
      text(
        root.ref.fileName,
        formatFilename(root.query('GET_ITEM_NAME', props.id))
      );
    };

    var didRemoveItem = function didRemoveItem(_ref3) {
      var root = _ref3.root;

      root.ref.data.removeAttribute('value');
    };

    var didCompleteItemProcessing = function didCompleteItemProcessing(_ref4) {
      var root = _ref4.root,
        action = _ref4.action;

      root.ref.data.value = action.serverFileReference;
    };

    var didRevertItemProcessing = function didRevertItemProcessing(_ref5) {
      var root = _ref5.root;

      root.ref.data.removeAttribute('value');
    };

    var fileWrapper = createView({
      create: create$5,
      ignoreRect: true,
      write: createRoute({
        DID_LOAD_ITEM: didLoadItem,
        DID_REMOVE_ITEM: didRemoveItem,
        DID_COMPLETE_ITEM_PROCESSING: didCompleteItemProcessing,
        DID_REVERT_ITEM_PROCESSING: didRevertItemProcessing
      }),
      didCreateView: function didCreateView(root) {
        applyFilters('CREATE_VIEW', Object.assign({}, root, { view: root }));
      },
      tag: 'fieldset',
      name: 'file-wrapper'
    });

    var PANEL_SPRING_PROPS = { type: 'spring', damping: 0.6, mass: 7 };

    var create$11 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      [
        {
          name: 'top'
        },
        {
          name: 'center',
          props: {
            translateY: null,
            scaleY: null
          },
          mixins: {
            animations: {
              scaleY: PANEL_SPRING_PROPS
            },
            styles: ['translateY', 'scaleY']
          }
        },
        {
          name: 'bottom',
          props: {
            translateY: null
          },
          mixins: {
            animations: {
              translateY: PANEL_SPRING_PROPS
            },
            styles: ['translateY']
          }
        }
      ].forEach(function(section) {
        createSection(root, section, props.name);
      });

      root.element.classList.add('filepond--' + props.name);

      root.ref.scalable = null;
    };

    var createSection = function createSection(root, section, className) {
      var viewConstructor = createView({
        name: 'panel-' + section.name + ' filepond--' + className,
        mixins: section.mixins,
        ignoreRectUpdate: true
      });

      var view = root.createChildView(viewConstructor, section.props);

      root.ref[section.name] = root.appendChildView(view);
    };

    var write$7 = function write(_ref2) {
      var root = _ref2.root,
        props = _ref2.props;

      // update scalable state
      if (root.ref.scalable === null || props.scalable !== root.ref.scalable) {
        root.ref.scalable = isBoolean(props.scalable) ? props.scalable : true;
        root.element.dataset.scalable = root.ref.scalable;
      }

      // no height, can't set
      if (!props.height) {
        return;
      }

      // get child rects
      var topRect = root.ref.top.rect.element;
      var bottomRect = root.ref.bottom.rect.element;

      // make sure height never is smaller than bottom and top seciton heights combined (will probably never happen, but who knows)
      var height = Math.max(topRect.height + bottomRect.height, props.height);

      // offset center part
      root.ref.center.translateY = topRect.height;

      // scale center part
      // use math ceil to prevent transparent lines because of rounding errors
      root.ref.center.scaleY =
        (height - topRect.height - bottomRect.height) / 100;

      // offset bottom part
      root.ref.bottom.translateY = height - bottomRect.height;
    };

    var panel = createView({
      name: 'panel',
      write: write$7,
      create: create$11,
      ignoreRect: true,
      mixins: {
        apis: ['height', 'scalable']
      }
    });

    /**
     * Creates the file view
     */
    var create$4 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // file view
      root.ref.controls = root.appendChildView(
        root.createChildView(fileWrapper, { id: props.id })
      );

      // file panel
      root.ref.panel = root.appendChildView(
        root.createChildView(panel, { name: 'item-panel' })
      );

      // default start height
      root.ref.panel.height = 0;

      // by default not marked for removal
      props.markedForRemoval = false;
    };

    var StateMap = {
      DID_START_ITEM_LOAD: 'busy',
      DID_UPDATE_ITEM_LOAD_PROGRESS: 'loading',
      DID_THROW_ITEM_INVALID: 'load-invalid',
      DID_THROW_ITEM_LOAD_ERROR: 'load-error',
      DID_LOAD_ITEM: 'idle',
      DID_THROW_ITEM_REMOVE_ERROR: 'remove-error',
      DID_START_ITEM_REMOVE: 'busy',
      DID_START_ITEM_PROCESSING: 'busy',
      DID_REQUEST_ITEM_PROCESSING: 'busy',
      DID_UPDATE_ITEM_PROCESS_PROGRESS: 'processing',
      DID_COMPLETE_ITEM_PROCESSING: 'processing-complete',
      DID_THROW_ITEM_PROCESSING_ERROR: 'processing-error',
      DID_ABORT_ITEM_PROCESSING: 'cancelled',
      DID_REVERT_ITEM_PROCESSING: 'idle'
    };

    var write$3 = function write(_ref2) {
      var root = _ref2.root,
        actions = _ref2.actions,
        props = _ref2.props;

      // update panel height
      root.ref.panel.height = root.ref.controls.rect.inner.height;

      // set own height
      var aspectRatio = root.query('GET_PANEL_ASPECT_RATIO');
      var allowMultiple = root.query('GET_ALLOW_MULTIPLE');
      if (aspectRatio && !allowMultiple) {
        root.height = root.rect.element.width * aspectRatio;
      } else {
        root.height = root.ref.controls.rect.inner.height;
      }

      // select last state change action
      var action = []
        .concat(toConsumableArray(actions))
        .filter(function(action) {
          return /^DID_/.test(action.type);
        })
        .reverse()
        .find(function(action) {
          return StateMap[action.type];
        });

      // no need to set same state twice
      if (!action || (action && action.type === props.currentState)) {
        return;
      }

      // set current state
      props.currentState = action.type;

      // set state
      root.element.dataset.filepondItemState = StateMap[props.currentState] || '';
    };

    var item = createView({
      create: create$4,
      write: write$3,
      destroy: function destroy(_ref3) {
        var root = _ref3.root,
          props = _ref3.props;

        root.dispatch('RELEASE_ITEM', { query: props.id });
      },
      tag: 'li',
      name: 'item',
      mixins: {
        apis: ['id', 'markedForRemoval'],
        styles: [
          'translateX',
          'translateY',
          'scaleX',
          'scaleY',
          'opacity',
          'height'
        ],
        animations: {
          scaleX: 'spring',
          scaleY: 'spring',
          translateX: 'spring',
          translateY: 'spring',
          opacity: { type: 'tween', duration: 150 }
        }
      }
    });

    var create$3 = function create(_ref) {
      var root = _ref.root;

      // need to set role to list as otherwise it won't be read as a list by VoiceOver
      attr(root.element, 'role', 'list');
    };

    /**
     * Inserts a new item
     * @param root
     * @param action
     */
    var addItemView = function addItemView(_ref2) {
      var root = _ref2.root,
        action = _ref2.action;
      var id = action.id,
        index = action.index,
        interactionMethod = action.interactionMethod;

      var animation = {
        opacity: 0
      };

      if (interactionMethod === InteractionMethod.NONE) {
        animation.translateY = null;
      }

      if (interactionMethod === InteractionMethod.DROP) {
        animation.scaleX = 0.8;
        animation.scaleY = 0.8;
        animation.translateY = null;
      }

      if (interactionMethod === InteractionMethod.BROWSE) {
        animation.translateY = -30;
      }

      if (interactionMethod === InteractionMethod.API) {
        animation.translateX = -100;
        animation.translateY = null;
      }

      root.appendChildView(
        root.createChildView(
          // view type
          item,

          // props
          Object.assign(
            {
              id: id
            },
            animation
          )
        ),
        index
      );
    };

    /**
     * Removes an existing item
     * @param root
     * @param action
     */
    var removeItemView = function removeItemView(_ref3) {
      var root = _ref3.root,
        action = _ref3.action;
      var id = action.id;

      // get the view matching the given id

      var view = root.childViews.find(function(child) {
        return child.id === id;
      });

      // if no view found, exit
      if (!view) {
        return;
      }

      // animate view out of view
      view.scaleX = 0.9;
      view.scaleY = 0.9;
      view.opacity = 0;

      // mark for removal
      view.markedForRemoval = true;
    };

    /**
     * Setup action routes
     */
    var route$2 = createRoute({
      DID_ADD_ITEM: addItemView,
      DID_REMOVE_ITEM: removeItemView
    });

    var dragTranslation = function dragTranslation(
      childIndex,
      dragIndex,
      itemMargin
    ) {
      if (childIndex - 1 === dragIndex) {
        return itemMargin / 6;
      }

      if (childIndex === dragIndex) {
        return itemMargin / 2;
      }

      if (childIndex + 1 === dragIndex) {
        return -itemMargin / 2;
      }

      if (childIndex + 2 === dragIndex) {
        return -itemMargin / 6;
      }

      return 0;
    };

    var easeOutCirc = function easeOutCirc(t) {
      var t1 = t - 1;
      return Math.sqrt(1 - t1 * t1);
    };

    var read = function read(_ref4) {
      var root = _ref4.root;

      var total = 0;

      root.childViews
        .filter(function(child) {
          return child.rect.outer.height;
        })
        .forEach(function(child) {
          var height =
            child.rect.element.height + child.rect.element.marginBottom;
          total += child.markedForRemoval
            ? height * easeOutCirc(child.opacity)
            : height;
        });

      root.rect.outer.height = total;
      root.rect.outer.bottom = root.rect.outer.height;
    };

    /**
     * Write to view
     * @param root
     * @param actions
     * @param props
     */
    var write$2 = function write(_ref5) {
      var root = _ref5.root,
        props = _ref5.props,
        actions = _ref5.actions;

      // route actions
      route$2({ root: root, props: props, actions: actions });

      var resting = true;

      // update item positions
      var offset = 0;
      root.childViews
        .filter(function(child) {
          return child.rect.outer.height;
        })
        .forEach(function(child, childIndex) {
          var childRect = child.rect;

          // set this child offset
          child.translateX = 0;
          child.translateY =
            offset +
            (props.dragIndex > -1
              ? dragTranslation(childIndex, props.dragIndex, 10)
              : 0);

          // show child if it's not marked for removal
          if (!child.markedForRemoval) {
            child.scaleX = 1;
            child.scaleY = 1;
            child.opacity = 1;
          }

          var itemHeight =
            childRect.element.height +
            childRect.element.marginTop +
            childRect.element.marginBottom;
          var height = child.markedForRemoval
            ? itemHeight * child.opacity
            : itemHeight;

          // calculate next child offset (reduce height by y scale for views that are being removed)
          offset += height;
        });

      return resting;
    };

    /**
     * Filters actions that are meant specifically for a certain child of the list
     * @param child
     * @param actions
     */
    var filterSetItemActions = function filterSetItemActions(child, actions) {
      return actions.filter(function(action) {
        // if action has an id, filter out actions that don't have this child id
        if (action.data && action.data.id) {
          return child.id === action.data.id;
        }

        // allow all other actions
        return true;
      });
    };

    var list = createView({
      create: create$3,
      write: write$2,
      read: read,
      tag: 'ul',
      name: 'list',
      didWriteView: function didWriteView(_ref6) {
        var root = _ref6.root;

        root.childViews
          .filter(function(view) {
            return view.markedForRemoval && view.opacity === 0 && view.resting;
          })
          .forEach(function(view) {
            view._destroy();
            root.removeChildView(view);
          });
      },
      filterFrameActionsForChild: filterSetItemActions,
      mixins: {
        apis: ['dragIndex']
      }
    });

    var getItemIndexByPosition = function getItemIndexByPosition(
      view,
      positionInView
    ) {
      var i = 0;
      var childViews = view.childViews;
      var l = childViews.length;
      for (; i < l; i++) {
        var item = childViews[i];
        var itemRect = item.rect.outer;
        var itemRectMid = itemRect.top + itemRect.height * 0.5;

        if (positionInView.top < itemRectMid) {
          return i;
        }
      }

      return l;
    };

    var create$2 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      root.ref.list = root.appendChildView(root.createChildView(list));

      props.dragCoordinates = null;
      props.overflowing = false;
    };

    var storeDragCoordinates = function storeDragCoordinates(_ref2) {
      var root = _ref2.root,
        props = _ref2.props,
        action = _ref2.action;

      props.dragCoordinates = {
        left: action.position.scopeLeft,
        top:
          action.position.scopeTop -
          root.rect.outer.top +
          root.rect.element.scrollTop
      };
    };

    var clearDragCoordinates = function clearDragCoordinates(_ref3) {
      var props = _ref3.props;

      props.dragCoordinates = null;
    };

    var route$1 = createRoute({
      DID_DRAG: storeDragCoordinates,
      DID_END_DRAG: clearDragCoordinates
    });

    var write$1 = function write(_ref4) {
      var root = _ref4.root,
        props = _ref4.props,
        actions = _ref4.actions;

      // route actions
      route$1({ root: root, props: props, actions: actions });

      // current drag position
      root.ref.list.dragIndex = props.dragCoordinates
        ? getItemIndexByPosition(root.ref.list, props.dragCoordinates)
        : -1;

      // if currently overflowing but no longer received overflow
      if (props.overflowing && !props.overflow) {
        props.overflowing = false;

        // reset overflow state
        root.element.dataset.state = '';
        root.height = null;
      }

      // if is not overflowing currently but does receive overflow value
      // !props.overflowing &&
      if (props.overflow) {
        var newHeight = Math.round(props.overflow);
        if (newHeight !== root.height) {
          props.overflowing = true;
          root.element.dataset.state = 'overflow';
          root.height = newHeight;
        }
      }
    };

    var listScroller = createView({
      create: create$2,
      write: write$1,
      name: 'list-scroller',
      mixins: {
        apis: ['overflow'],
        styles: ['height', 'translateY'],
        animations: {
          translateY: 'spring'
        }
      }
    });

    var attrToggle = function attrToggle(element, name, state) {
      var enabledValue =
        arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';

      if (state) {
        attr(element, name, enabledValue);
      } else {
        element.removeAttribute(name);
      }
    };

    var resetFileInput = function resetFileInput(input) {
      // no value, no need to reset
      if (!input || input.value === '') {
        return;
      }

      try {
        // for modern browsers
        input.value = '';
      } catch (err) {}

      // for IE10
      if (input.value) {
        // quickly append input to temp form and reset form
        var form = createElement$1('form');
        var parentNode = input.parentNode;
        var ref = input.nextSibling;
        form.appendChild(input);
        form.reset();

        // re-inject input where it originally was
        if (ref) {
          parentNode.insertBefore(input, ref);
        } else {
          parentNode.appendChild(input);
        }
      }
    };

    var create$12 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // set id so can be referenced from outside labels
      root.element.id = 'filepond--browser-' + props.id;

      // we have to link this element to the status element
      attr(root.element, 'aria-controls', 'filepond--assistant-' + props.id);

      // set label, we use labelled by as otherwise the screenreader does not read the "browse" text in the label (as it has tabindex: 0)
      attr(root.element, 'aria-labelledby', 'filepond--drop-label-' + props.id);

      // handle changes to the input field
      root.ref.handleChange = function(e) {
        if (!root.element.value) {
          return;
        }

        // extract files
        var files = [].concat(toConsumableArray(root.element.files));

        // we add a little delay so the OS file select window can move out of the way before we add our file
        setTimeout(function() {
          // load files
          props.onload(files);

          // reset input, it's just for exposing a method to drop files, should not retain any state
          resetFileInput(root.element);
        }, 250);
      };
      root.element.addEventListener('change', root.ref.handleChange);
    };

    var setAcceptedFileTypes = function setAcceptedFileTypes(_ref2) {
      var root = _ref2.root,
        action = _ref2.action;

      attrToggle(
        root.element,
        'accept',
        !!action.value,
        action.value ? action.value.join(',') : ''
      );
    };

    var toggleAllowMultiple = function toggleAllowMultiple(_ref3) {
      var root = _ref3.root,
        action = _ref3.action;

      attrToggle(root.element, 'multiple', action.value);
    };

    var toggleAllowBrowse$1 = function toggleAllowBrowse(_ref4) {
      var root = _ref4.root,
        action = _ref4.action;

      attrToggle(root.element, 'disabled', !action.value);
    };

    var toggleRequired = function toggleRequired(_ref5) {
      var root = _ref5.root,
        action = _ref5.action;

      // want to remove required, always possible
      if (!action.value) {
        attrToggle(root.element, 'required', false);
      } else if (root.query('GET_TOTAL_ITEMS') === 0) {
        // if want to make required, only possible when zero items
        attrToggle(root.element, 'required', true);
      }
    };

    var setCaptureMethod = function setCaptureMethod(_ref6) {
      var root = _ref6.root,
        action = _ref6.action;

      attrToggle(
        root.element,
        'capture',
        !!action.value,
        action.value === true ? '' : action.value
      );
    };

    var updateRequiredStatus = function updateRequiredStatus(_ref8) {
      var root = _ref8.root;

      // always remove the required attribute when more than zero items
      if (root.query('GET_TOTAL_ITEMS') > 0) {
        attrToggle(root.element, 'required', false);
      } else if (root.query('GET_REQUIRED')) {
        // if zero items, we only add it if the field is required
        attrToggle(root.element, 'required', true);
      }
    };

    var browser = createView({
      tag: 'input',
      name: 'browser',
      ignoreRect: true,
      ignoreRectUpdate: true,
      attributes: {
        type: 'file'
      },
      create: create$12,
      destroy: function destroy(_ref9) {
        var root = _ref9.root;

        root.element.removeEventListener('change', root.ref.handleChange);
      },
      write: createRoute({
        DID_ADD_ITEM: updateRequiredStatus,
        DID_REMOVE_ITEM: updateRequiredStatus,
        DID_SET_ALLOW_BROWSE: toggleAllowBrowse$1,
        DID_SET_ALLOW_MULTIPLE: toggleAllowMultiple,
        DID_SET_ACCEPTED_FILE_TYPES: setAcceptedFileTypes,
        DID_SET_CAPTURE_METHOD: setCaptureMethod,
        DID_SET_REQUIRED: toggleRequired
      })
    });

    var Key = {
      ENTER: 13,
      SPACE: 32
    };

    var create$13 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // create the label and link it to the file browser
      var label = createElement$1('label');
      attr(label, 'for', 'filepond--browser-' + props.id);

      // use for labeling file input (aria-labelledby on file input)
      attr(label, 'id', 'filepond--drop-label-' + props.id);

      // hide the label from screenreaders, the input element has an aria-label
      attr(label, 'aria-hidden', 'true');

      // handle keys
      label.addEventListener('keydown', function(e) {
        if (e.keyCode === Key.ENTER || e.keyCode === Key.SPACE) {
          // stops from triggering the element a second time
          e.preventDefault();

          // click link (will then in turn activate file input)
          root.ref.label.click();
        }
      });

      // update
      updateLabelValue(label, props.caption);

      // add!
      root.appendChild(label);
      root.ref.label = label;
    };

    var updateLabelValue = function updateLabelValue(label, value) {
      label.innerHTML = value;
      var clickable = label.querySelector('.filepond--label-action');
      if (clickable) {
        attr(clickable, 'tabindex', '0');
      }
      return value;
    };

    var dropLabel = createView({
      name: 'drop-label',
      ignoreRect: true,
      create: create$13,
      write: createRoute({
        DID_SET_LABEL_IDLE: function DID_SET_LABEL_IDLE(_ref2) {
          var root = _ref2.root,
            action = _ref2.action;

          updateLabelValue(root.ref.label, action.value);
        }
      }),
      mixins: {
        styles: ['opacity', 'translateX', 'translateY'],
        animations: {
          opacity: { type: 'tween', duration: 150 },
          translateX: 'spring',
          translateY: 'spring'
        }
      }
    });

    var blob = createView({
      name: 'drip-blob',
      ignoreRect: true,
      mixins: {
        styles: ['translateX', 'translateY', 'scaleX', 'scaleY', 'opacity'],
        animations: {
          scaleX: 'spring',
          scaleY: 'spring',
          translateX: 'spring',
          translateY: 'spring',
          opacity: { type: 'tween', duration: 250 }
        }
      }
    });

    var addBlob = function addBlob(_ref) {
      var root = _ref.root;

      var centerX = root.rect.element.width * 0.5;
      var centerY = root.rect.element.height * 0.5;

      root.ref.blob = root.appendChildView(
        root.createChildView(blob, {
          opacity: 0,
          scaleX: 2.5,
          scaleY: 2.5,
          translateX: centerX,
          translateY: centerY
        })
      );
    };

    var moveBlob = function moveBlob(_ref2) {
      var root = _ref2.root,
        action = _ref2.action;

      if (!root.ref.blob) {
        addBlob({ root: root });
        return;
      }

      root.ref.blob.translateX = action.position.scopeLeft;
      root.ref.blob.translateY = action.position.scopeTop;
      root.ref.blob.scaleX = 1;
      root.ref.blob.scaleY = 1;
      root.ref.blob.opacity = 1;
    };

    var hideBlob = function hideBlob(_ref3) {
      var root = _ref3.root;

      if (!root.ref.blob) {
        return;
      }
      root.ref.blob.opacity = 0;
    };

    var explodeBlob = function explodeBlob(_ref4) {
      var root = _ref4.root;

      if (!root.ref.blob) {
        return;
      }
      root.ref.blob.scaleX = 2.5;
      root.ref.blob.scaleY = 2.5;
      root.ref.blob.opacity = 0;
    };

    var write$8 = function write(_ref5) {
      var root = _ref5.root,
        props = _ref5.props,
        actions = _ref5.actions;

      route$4({ root: root, props: props, actions: actions });

      var blob$$1 = root.ref.blob;

      if (actions.length === 0 && blob$$1 && blob$$1.opacity === 0) {
        root.removeChildView(blob$$1);
        root.ref.blob = null;
      }
    };

    var route$4 = createRoute({
      DID_DRAG: moveBlob,
      DID_DROP: explodeBlob,
      DID_END_DRAG: hideBlob
    });

    var drip = createView({
      ignoreRect: true,
      ignoreRectUpdate: true,
      name: 'drip',
      write: write$8
    });

    var getRootNode = function getRootNode(element) {
      return 'getRootNode' in element ? element.getRootNode() : document;
    };

    var requestDataTransferItems = function requestDataTransferItems(
      dataTransfer
    ) {
      return new Promise(function(resolve, reject) {
        // try to get links from transfer, if found, we'll exit immidiately
        // as only one link can be dragged at once
        var links = getLinks(dataTransfer);
        if (links.length) {
          resolve(links);
          return;
        }

        // try to get files from the transfer
        getFiles(dataTransfer).then(resolve);
      });
    };

    /**
     * Extracts files from a DataTransfer object
     */
    var getFiles = function getFiles(dataTransfer) {
      return new Promise(function(resolve, reject) {
        // get the transfer items as promises
        var promisedFiles = (dataTransfer.items
          ? [].concat(toConsumableArray(dataTransfer.items))
          : []
        )
          // only keep file system items (files and directories)
          .filter(function(item) {
            return isFileSystemItem(item);
          })

          // map each item to promise
          .map(function(item) {
            return getFilesFromItem(item);
          });

        // if is empty, see if we can extract some info from the files property as a fallback
        if (!promisedFiles.length) {
          // TODO: test for directories (should not be allowed)
          // Use FileReader, problem is that the files property gets lost in the process

          resolve(
            dataTransfer.files
              ? [].concat(toConsumableArray(dataTransfer.files))
              : []
          );
          return;
        }

        // done!
        Promise.all(promisedFiles).then(function(returendFileGroups) {
          // flatten groups
          var files = [];
          returendFileGroups.forEach(function(group) {
            files.push.apply(files, toConsumableArray(group));
          });

          // done (filter out empty files)!
          resolve(
            files.filter(function(file) {
              return file;
            })
          );
        });
      });
    };

    var isFileSystemItem = function isFileSystemItem(item) {
      if (isEntry(item)) {
        var entry = getAsEntry(item);
        if (entry) {
          return entry.isFile || entry.isDirectory;
        }
      }
      return item.kind === 'file';
    };

    var getFilesFromItem = function getFilesFromItem(item) {
      return new Promise(function(resolve, reject) {
        if (isDirectoryEntry(item)) {
          getFilesInDirectory(getAsEntry(item)).then(resolve);
          return;
        }

        resolve([item.getAsFile()]);
      });
    };

    var getFilesInDirectory = function getFilesInDirectory(entry) {
      return new Promise(function(resolve, reject) {
        var files = [];

        // the total entries to read
        var totalFilesFound = 0;

        // the recursive function
        var readEntries = function readEntries(dirEntry) {
          var directoryReader = dirEntry.createReader();
          directoryReader.readEntries(function(entries) {
            entries.forEach(function(entry) {
              // recursively read more directories
              if (entry.isDirectory) {
                readEntries(entry);
              } else {
                // read as file
                totalFilesFound++;
                entry.file(function(file) {
                  files.push(file);

                  if (totalFilesFound === files.length) {
                    resolve(files);
                  }
                });
              }
            });
          });
        };

        // go!
        readEntries(entry);
      });
    };

    var isDirectoryEntry = function isDirectoryEntry(item) {
      return isEntry(item) && (getAsEntry(item) || {}).isDirectory;
    };

    var isEntry = function isEntry(item) {
      return 'webkitGetAsEntry' in item;
    };

    var getAsEntry = function getAsEntry(item) {
      return item.webkitGetAsEntry();
    };

    /**
     * Extracts links from a DataTransfer object
     */
    var getLinks = function getLinks(dataTransfer) {
      var links = [];
      try {
        // look in meta data property
        links = getLinksFromTransferMetaData(dataTransfer);
        if (links.length) {
          return links;
        }
        links = getLinksFromTransferURLData(dataTransfer);
      } catch (e) {
        // nope nope nope (probably IE trouble)
      }
      return links;
    };

    var getLinksFromTransferURLData = function getLinksFromTransferURLData(
      dataTransfer
    ) {
      var data = dataTransfer.getData('url');
      if (typeof data === 'string' && data.length) {
        return [data];
      }
      return [];
    };

    var getLinksFromTransferMetaData = function getLinksFromTransferMetaData(
      dataTransfer
    ) {
      var data = dataTransfer.getData('text/html');
      if (typeof data === 'string' && data.length) {
        var matches = data.match(/src\s*=\s*"(.+?)"/);
        if (matches) {
          return [matches[1]];
        }
      }
      return [];
    };

    var dragNDropObservers = [];

    var eventPosition = function eventPosition(e) {
      return {
        pageLeft: e.pageX,
        pageTop: e.pageY,
        scopeLeft: e.offsetX || e.layerX,
        scopeTop: e.offsetY || e.layerY
      };
    };

    var createDragNDropClient = function createDragNDropClient(
      element,
      scopeToObserve,
      filterElement
    ) {
      var observer = getDragNDropObserver(scopeToObserve);

      var client = {
        element: element,
        filterElement: filterElement,
        state: null,
        ondrop: function ondrop() {},
        onenter: function onenter() {},
        ondrag: function ondrag() {},
        onexit: function onexit() {},
        onload: function onload() {},
        allowdrop: function allowdrop() {}
      };

      client.destroy = observer.addListener(client);

      return client;
    };

    var getDragNDropObserver = function getDragNDropObserver(element) {
      // see if already exists, if so, return
      var observer = dragNDropObservers.find(function(item) {
        return item.element === element;
      });
      if (observer) {
        return observer;
      }

      // create new observer, does not yet exist for this element
      var newObserver = createDragNDropObserver(element);
      dragNDropObservers.push(newObserver);
      return newObserver;
    };

    var createDragNDropObserver = function createDragNDropObserver(element) {
      var clients = [];

      var routes = {
        dragenter: dragenter,
        dragover: dragover,
        dragleave: dragleave,
        drop: drop
      };

      var handlers = {};

      forin(routes, function(event, createHandler) {
        handlers[event] = createHandler(element, clients);
        element.addEventListener(event, handlers[event], false);
      });

      var observer = {
        element: element,
        addListener: function addListener(client) {
          // add as client
          clients.push(client);

          // return removeListener function
          return function() {
            // remove client
            clients.splice(clients.indexOf(client), 1);

            // if no more clients, clean up observer
            if (clients.length === 0) {
              dragNDropObservers.splice(dragNDropObservers.indexOf(observer), 1);

              forin(routes, function(event) {
                element.removeEventListener(event, handlers[event], false);
              });
            }
          };
        }
      };

      return observer;
    };

    var elementFromPoint = function elementFromPoint(root, point) {
      if (!('elementFromPoint' in root)) {
        root = document;
      }
      return root.elementFromPoint(point.x, point.y);
    };

    var isEventTarget = function isEventTarget(e, target) {
      // get root
      var root = getRootNode(target);

      // get element at position
      // if root is not actual shadow DOM and does not have elementFromPoint method, use the one on document
      var elementAtPosition = elementFromPoint(root, {
        x: e.pageX - window.pageXOffset,
        y: e.pageY - window.pageYOffset
      });

      // test if target is the element or if one of its children is
      return elementAtPosition === target || target.contains(elementAtPosition);
    };

    var initialTarget = null;

    var setDropEffect = function setDropEffect(dataTransfer, effect) {
      // is in try catch as IE11 will throw error if not
      try {
        dataTransfer.dropEffect = effect;
      } catch (e) {}
    };

    var dragenter = function dragenter(root, clients) {
      return function(e) {
        e.preventDefault();

        initialTarget = e.target;

        clients.forEach(function(client) {
          var element = client.element,
            onenter = client.onenter;

          if (isEventTarget(e, element)) {
            client.state = 'enter';

            // fire enter event
            onenter(eventPosition(e));
          }
        });
      };
    };

    var dragover = function dragover(root, clients) {
      return function(e) {
        e.preventDefault();

        var dataTransfer = e.dataTransfer;

        requestDataTransferItems(dataTransfer).then(function(items) {
          var overDropTarget = false;

          clients.some(function(client) {
            var filterElement = client.filterElement,
              element = client.element,
              onenter = client.onenter,
              onexit = client.onexit,
              ondrag = client.ondrag,
              allowdrop = client.allowdrop;

            // by default we can drop

            setDropEffect(dataTransfer, 'copy');

            // allow transfer of these items
            var allowsTransfer = allowdrop(items);

            // only used when can be dropped on page
            if (!allowsTransfer) {
              setDropEffect(dataTransfer, 'none');
              return;
            }

            // targetting this client
            if (isEventTarget(e, element)) {
              overDropTarget = true;

              // had no previous state, means we are entering this client
              if (client.state === null) {
                client.state = 'enter';
                onenter(eventPosition(e));
                return;
              }

              // now over element (no matter if it allows the drop or not)
              client.state = 'over';

              // needs to allow transfer
              if (filterElement && !allowsTransfer) {
                setDropEffect(dataTransfer, 'none');
                return;
              }

              // dragging
              ondrag(eventPosition(e));
            } else {
              // should be over an element to drop
              if (filterElement && !overDropTarget) {
                setDropEffect(dataTransfer, 'none');
              }

              // might have just left this client?
              if (client.state) {
                client.state = null;
                onexit(eventPosition(e));
              }
            }
          });
        });
      };
    };

    var drop = function drop(root, clients) {
      return function(e) {
        e.preventDefault();

        var dataTransfer = e.dataTransfer;
        requestDataTransferItems(dataTransfer).then(function(items) {
          clients.forEach(function(client) {
            var filterElement = client.filterElement,
              element = client.element,
              ondrop = client.ondrop,
              onexit = client.onexit,
              allowdrop = client.allowdrop;

            client.state = null;

            var allowsTransfer = allowdrop(items);

            // no transfer for this client
            if (!allowsTransfer) {
              onexit(eventPosition(e));
              return;
            }

            // if we're filtering on element we need to be over the element to drop
            if (filterElement && !isEventTarget(e, element)) {
              return;
            }

            ondrop(eventPosition(e), items);
          });
        });
      };
    };

    var dragleave = function dragleave(root, clients) {
      return function(e) {
        if (initialTarget !== e.target) {
          return;
        }

        clients.forEach(function(client) {
          var onexit = client.onexit;

          client.state = null;

          onexit(eventPosition(e));
        });
      };
    };

    var createHopper = function createHopper(scope, validateItems, options) {
      // is now hopper scope
      scope.classList.add('filepond--hopper');

      // shortcuts
      var catchesDropsOnPage = options.catchesDropsOnPage,
        requiresDropOnElement = options.requiresDropOnElement;

      // create a dnd client

      var client = createDragNDropClient(
        scope,
        catchesDropsOnPage ? document.documentElement : scope,
        requiresDropOnElement
      );

      // current client state
      var lastState = '';
      var currentState = '';

      // determines if a file may be dropped
      client.allowdrop = function(items) {
        // TODO: if we can, throw error to indicate the items cannot by dropped

        return validateItems(items);
      };

      client.ondrop = function(position, items) {
        if (!validateItems(items)) {
          api.ondragend(position);
          return;
        }

        currentState = 'drag-drop';

        api.onload(items, position);
      };

      client.ondrag = function(position) {
        api.ondrag(position);
      };

      client.onenter = function(position) {
        currentState = 'drag-over';

        api.ondragstart(position);
      };

      client.onexit = function(position) {
        currentState = 'drag-exit';

        api.ondragend(position);
      };

      var api = {
        updateHopperState: function updateHopperState() {
          if (lastState !== currentState) {
            scope.dataset.hopperState = currentState;
            lastState = currentState;
          }
        },
        onload: function onload() {},
        ondragstart: function ondragstart() {},
        ondrag: function ondrag() {},
        ondragend: function ondragend() {},
        destroy: function destroy() {
          // destroy client
          client.destroy();
        }
      };

      return api;
    };

    var listening = false;
    var listeners$1 = [];

    var handlePaste = function handlePaste(e) {
      requestDataTransferItems(e.clipboardData).then(function(files) {
        // no files received
        if (!files.length) {
          return;
        }

        // notify listeners of received files
        listeners$1.forEach(function(listener) {
          return listener(files);
        });
      });
    };

    var listen = function listen(cb) {
      // can't add twice
      if (listeners$1.includes(cb)) {
        return;
      }

      // add initial listener
      listeners$1.push(cb);

      // setup paste listener for entire page
      if (listening) {
        return;
      }

      listening = true;
      document.addEventListener('paste', handlePaste);
    };

    var unlisten = function unlisten(listener) {
      arrayRemove(listeners$1, listeners$1.indexOf(listener));

      // clean up
      if (listeners$1.length === 0) {
        document.removeEventListener('paste', handlePaste);
        listening = false;
      }
    };

    var createPaster = function createPaster() {
      var cb = function cb(files) {
        api.onload(files);
      };

      var api = {
        destroy: function destroy() {
          unlisten(cb);
        },
        onload: function onload() {}
      };

      listen(cb);

      return api;
    };

    var debounce = function debounce(func) {
      var interval =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 16;
      var immidiateOnly =
        arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;

      var last = Date.now();
      var timeout = null;

      return function() {
        for (
          var _len = arguments.length, args = Array(_len), _key = 0;
          _key < _len;
          _key++
        ) {
          args[_key] = arguments[_key];
        }

        clearTimeout(timeout);

        var dist = Date.now() - last;

        var fn = function fn() {
          last = Date.now();
          func.apply(undefined, args);
        };

        if (dist < interval) {
          // we need to delay by the difference between interval and dist
          // for example: if distance is 10 ms and interval is 16 ms,
          // we need to wait an additional 6ms before calling the function)
          if (!immidiateOnly) {
            timeout = setTimeout(fn, interval - dist);
          }
        } else {
          // go!
          fn();
        }
      };
    };

    /**
     * Creates the file view
     */
    var create$14 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      root.element.id = 'filepond--assistant-' + props.id;
      attr(root.element, 'role', 'status');
      attr(root.element, 'aria-live', 'polite');
      attr(root.element, 'aria-relevant', 'additions');
    };

    var addFilesNotificationTimeout = null;
    var notificationClearTimeout = null;

    var filenames = [];

    var assist = function assist(root, message) {
      root.element.textContent = message;
    };

    var clear$1 = function clear(root) {
      root.element.textContent = '';
    };

    var listModified = function listModified(root, filename, label) {
      var total = root.query('GET_TOTAL_ITEMS');
      assist(
        root,
        label +
          ' ' +
          filename +
          ', ' +
          total +
          ' ' +
          (total === 1
            ? root.query('GET_LABEL_FILE_COUNT_SINGULAR')
            : root.query('GET_LABEL_FILE_COUNT_PLURAL'))
      );

      // clear group after set amount of time so the status is not read twice
      clearTimeout(notificationClearTimeout);
      notificationClearTimeout = setTimeout(function() {
        clear$1(root);
      }, 1500);
    };

    var isUsingFilePond = function isUsingFilePond(root) {
      return root.element.parentNode.contains(document.activeElement);
    };

    var itemAdded = function itemAdded(_ref2) {
      var root = _ref2.root,
        action = _ref2.action;

      if (!isUsingFilePond(root)) {
        return;
      }

      root.element.textContent = '';
      var item = root.query('GET_ITEM', action.id);
      filenames.push(item.filename);

      clearTimeout(addFilesNotificationTimeout);
      addFilesNotificationTimeout = setTimeout(function() {
        listModified(
          root,
          filenames.join(', '),
          root.query('GET_LABEL_FILE_ADDED')
        );
        filenames.length = 0;
      }, 750);
    };

    var itemRemoved = function itemRemoved(_ref3) {
      var root = _ref3.root,
        action = _ref3.action;

      if (!isUsingFilePond(root)) {
        return;
      }

      var item = action.item;
      listModified(root, item.filename, root.query('GET_LABEL_FILE_REMOVED'));
    };

    var itemProcessed = function itemProcessed(_ref4) {
      var root = _ref4.root,
        action = _ref4.action;

      // will also notify the user when FilePond is not being used, as the user might be occupied with other activities while uploading a file

      var item = root.query('GET_ITEM', action.id);
      var filename = item.filename;
      var label = root.query('GET_LABEL_FILE_PROCESSING_COMPLETE');

      assist(root, filename + ' ' + label);
    };

    var itemProcessedUndo = function itemProcessedUndo(_ref5) {
      var root = _ref5.root,
        action = _ref5.action;

      var item = root.query('GET_ITEM', action.id);
      var filename = item.filename;
      var label = root.query('GET_LABEL_FILE_PROCESSING_ABORTED');

      assist(root, filename + ' ' + label);
    };

    var itemError = function itemError(_ref6) {
      var root = _ref6.root,
        action = _ref6.action;

      var item = root.query('GET_ITEM', action.id);
      var filename = item.filename;

      // will also notify the user when FilePond is not being used, as the user might be occupied with other activities while uploading a file

      assist(root, action.status.main + ' ' + filename + ' ' + action.status.sub);
    };

    var assistant = createView({
      create: create$14,
      ignoreRect: true,
      ignoreRectUpdate: true,
      write: createRoute({
        DID_LOAD_ITEM: itemAdded,
        DID_REMOVE_ITEM: itemRemoved,
        DID_COMPLETE_ITEM_PROCESSING: itemProcessed,

        DID_ABORT_ITEM_PROCESSING: itemProcessedUndo,
        DID_REVERT_ITEM_PROCESSING: itemProcessedUndo,

        DID_THROW_ITEM_REMOVE_ERROR: itemError,
        DID_THROW_ITEM_LOAD_ERROR: itemError,
        DID_THROW_ITEM_INVALID: itemError,
        DID_THROW_ITEM_PROCESSING_ERROR: itemError
      }),
      tag: 'span',
      name: 'assistant'
    });

    var toCamels = function toCamels(string) {
      var separator =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '-';
      return string.replace(new RegExp(separator + '.', 'g'), function(sub) {
        return sub.charAt(1).toUpperCase();
      });
    };

    var MAX_FILES_LIMIT = 1000000;

    var create$1 = function create(_ref) {
      var root = _ref.root,
        props = _ref.props;

      // Add id
      var id = root.query('GET_ID');
      if (id) {
        root.element.id = id;
      }

      // Add className
      var className = root.query('GET_CLASS_NAME');
      if (className) {
        className.split(' ').forEach(function(name) {
          root.element.classList.add(name);
        });
      }

      // Field label
      root.ref.label = root.appendChildView(
        root.createChildView(
          dropLabel,
          Object.assign({}, props, {
            translateY: null,
            caption: root.query('GET_LABEL_IDLE')
          })
        )
      );

      // List of items
      root.ref.list = root.appendChildView(
        root.createChildView(listScroller, { translateY: null })
      );

      // Background panel
      root.ref.panel = root.appendChildView(
        root.createChildView(panel, { name: 'panel-root' })
      );

      // Assistant notifies assistive tech when content changes
      root.ref.assistant = root.appendChildView(
        root.createChildView(assistant, Object.assign({}, props))
      );

      // Measure (tests if fixed height was set)
      // DOCTYPE needs to be set for this to work
      root.ref.measure = createElement$1('div');
      root.ref.measure.style.height = '100%';
      root.element.appendChild(root.ref.measure);

      // information on the root height or fixed height status
      root.ref.bounds = null;

      // apply initial style properties
      root
        .query('GET_STYLES')
        .filter(function(style) {
          return !isEmpty(style.value);
        })
        .map(function(_ref2) {
          var name = _ref2.name,
            value = _ref2.value;

          root.element.dataset[name] = value;
        });
    };

    var write = function write(_ref3) {
      var root = _ref3.root,
        props = _ref3.props,
        actions = _ref3.actions;

      // get box bounds, we do this only once
      var bounds = root.ref.bounds;
      if (!bounds) {
        bounds = root.ref.bounds = calculateRootBoundingBoxHeight(root);

        // destroy measure element
        root.element.removeChild(root.ref.measure);
        root.ref.measure = null;
      }

      // route actions
      route({ root: root, props: props, actions: actions });

      // apply style properties
      actions
        .filter(function(action) {
          return /^DID_SET_STYLE_/.test(action.type);
        })
        .filter(function(action) {
          return !isEmpty(action.data.value);
        })
        .map(function(_ref4) {
          var type = _ref4.type,
            data = _ref4.data;

          var name = toCamels(type.substr(8).toLowerCase(), '_');
          root.element.dataset[name] = data.value;
          root.invalidateLayout();
        });

      // get quick references to various high level parts of the upload tool
      var _root$ref = root.ref,
        hopper = _root$ref.hopper,
        label = _root$ref.label,
        list = _root$ref.list,
        panel$$1 = _root$ref.panel;

      // sets correct state to hopper scope

      if (hopper) {
        hopper.updateHopperState();
      }

      // bool to indicate if we're full or not
      var aspectRatio = root.query('GET_PANEL_ASPECT_RATIO');
      var isMultiItem = root.query('GET_ALLOW_MULTIPLE');
      var totalItems = root.query('GET_TOTAL_ITEMS');
      var maxItems = isMultiItem
        ? root.query('GET_MAX_FILES') || MAX_FILES_LIMIT
        : 1;
      var atMaxCapacity = totalItems === maxItems;

      // action used to add item
      var addAction = actions.find(function(action) {
        return action.type === 'DID_ADD_ITEM';
      });

      // if reached max capacity and we've just reached it
      if (atMaxCapacity && addAction) {
        // get interaction type
        var interactionMethod = addAction.data.interactionMethod;

        // hide label
        label.opacity = 0;

        if (isMultiItem) {
          label.translateY = -40;
        } else {
          if (interactionMethod === InteractionMethod.API) {
            label.translateX = 40;
          } else if (interactionMethod === InteractionMethod.BROWSE) {
            label.translateY = 40;
          } else {
            label.translateY = 30;
          }
        }
      } else if (!atMaxCapacity) {
        label.opacity = 1;
        label.translateX = 0;
        label.translateY = 0;
      }

      var listItemMargin = calculateListItemMargin(root);

      var listHeight = calculateListHeight(root, maxItems);
      var labelHeight = label.rect.element.height;
      var currentLabelHeight = !isMultiItem || atMaxCapacity ? 0 : labelHeight;

      var listMarginTop = atMaxCapacity ? list.rect.element.marginTop : 0;
      var listMarginBottom =
        totalItems === 0 ? 0 : list.rect.element.marginBottom;

      var visualHeight =
        currentLabelHeight + listMarginTop + listHeight.visual + listMarginBottom;
      var boundsHeight =
        currentLabelHeight + listMarginTop + listHeight.bounds + listMarginBottom;

      // link list to label bottom position
      list.translateY =
        Math.max(0, currentLabelHeight - list.rect.element.marginTop) -
        listItemMargin.top;

      if (aspectRatio) {
        // fixed aspect ratio

        // calculate height based on width
        var width = root.rect.element.width;
        var height = width * aspectRatio;

        // fix height of panel so it adheres to aspect ratio
        panel$$1.scalable = false;
        panel$$1.height = height;

        // available height for list
        var listAvailableHeight =
          // the height of the panel minus the label height
          height -
          currentLabelHeight -
          // the room we leave open between the end of the list and the panel bottom
          (listMarginBottom - listItemMargin.bottom) -
          // if we're full we need to leave some room between the top of the panel and the list
          (atMaxCapacity ? listMarginTop : 0);

        if (listHeight.visual > listAvailableHeight) {
          list.overflow = listAvailableHeight;
        } else {
          list.overflow = null;
        }

        // set container bounds (so pushes siblings downwards)
        root.height = height;
      } else if (bounds.fixedHeight) {
        // fixed height

        // fix height of panel
        panel$$1.scalable = false;

        // available height for list
        var _listAvailableHeight =
          // the height of the panel minus the label height
          bounds.fixedHeight -
          currentLabelHeight -
          // the room we leave open between the end of the list and the panel bottom
          (listMarginBottom - listItemMargin.bottom) -
          // if we're full we need to leave some room between the top of the panel and the list
          (atMaxCapacity ? listMarginTop : 0);

        // set list height
        if (listHeight.visual > _listAvailableHeight) {
          list.overflow = _listAvailableHeight;
        } else {
          list.overflow = null;
        }

        // no need to set container bounds as these are handles by CSS fixed height
      } else if (bounds.cappedHeight) {
        // max-height

        // not a fixed height panel
        var isCappedHeight = visualHeight >= bounds.cappedHeight;
        var panelHeight = Math.min(bounds.cappedHeight, visualHeight);
        panel$$1.scalable = true;
        panel$$1.height = isCappedHeight
          ? panelHeight
          : panelHeight - listItemMargin.top - listItemMargin.bottom;

        // available height for list
        var _listAvailableHeight2 =
          // the height of the panel minus the label height
          panelHeight -
          currentLabelHeight -
          // the room we leave open between the end of the list and the panel bottom
          (listMarginBottom - listItemMargin.bottom) -
          // if we're full we need to leave some room between the top of the panel and the list
          (atMaxCapacity ? listMarginTop : 0);

        // set list height (if is overflowing)
        if (
          visualHeight > bounds.cappedHeight &&
          listHeight.visual > _listAvailableHeight2
        ) {
          list.overflow = _listAvailableHeight2;
        } else {
          list.overflow = null;
        }

        // set container bounds (so pushes siblings downwards)
        root.height = Math.min(
          bounds.cappedHeight,
          boundsHeight - listItemMargin.top - listItemMargin.bottom
        );
      } else {
        // flexible height

        // not a fixed height panel
        var itemMargin =
          totalItems > 0 ? listItemMargin.top + listItemMargin.bottom : 0;
        panel$$1.scalable = true;
        panel$$1.height = Math.max(labelHeight, visualHeight - itemMargin);

        // set container bounds (so pushes siblings downwards)
        root.height = Math.max(labelHeight, boundsHeight - itemMargin);
      }
    };

    var calculateListItemMargin = function calculateListItemMargin(root) {
      var item = root.ref.list.childViews[0].childViews[0];
      return item
        ? {
            top: item.rect.element.marginTop,
            bottom: item.rect.element.marginBottom
          }
        : {
            top: 0,
            bottom: 0
          };
    };

    var calculateListHeight = function calculateListHeight(root, maxItems) {
      var visual = 0;
      var bounds = 0;

      root.ref.list.childViews[0].childViews.forEach(function(item, index) {
        // don't count items above max items list
        if (index >= maxItems) return;

        // calculate the total height of all items in the list
        var rect = item.rect.element;
        var itemHeight = rect.height + rect.marginTop + rect.marginBottom;
        bounds += itemHeight;
        visual += item.markedForRemoval ? item.opacity * itemHeight : itemHeight;
      });

      return {
        visual: visual,
        bounds: bounds
      };
    };

    var calculateRootBoundingBoxHeight = function calculateRootBoundingBoxHeight(
      root
    ) {
      var height = root.ref.measureHeight || null;
      var cappedHeight = parseInt(root.style.maxHeight, 10) || null;
      var fixedHeight = height === 0 ? null : height;

      return {
        cappedHeight: cappedHeight,
        fixedHeight: fixedHeight
      };
    };

    var exceedsMaxFiles = function exceedsMaxFiles(root, items) {
      var allowReplace = root.query('GET_ALLOW_REPLACE');
      var allowMultiple = root.query('GET_ALLOW_MULTIPLE');
      var totalItems = root.query('GET_TOTAL_ITEMS');
      var maxItems = root.query('GET_MAX_FILES');

      // total amount of items being dragged
      var totalBrowseItems = items.length;

      // if does not allow multiple items and dragging more than one item
      if (!allowMultiple && totalBrowseItems > 1) {
        return true;
      }

      // limit max items to one if not allowed to drop multiple items
      maxItems = allowMultiple ? maxItems : allowReplace ? maxItems : 1;

      // no more room?
      var hasMaxItems = isInt(maxItems);
      if (hasMaxItems && totalItems + totalBrowseItems > maxItems) {
        root.dispatch('DID_THROW_MAX_FILES', {
          source: items,
          error: createResponse('warning', 0, 'Max files')
        });
        return true;
      }

      return false;
    };

    var toggleAllowDrop = function toggleAllowDrop(_ref5) {
      var root = _ref5.root,
        props = _ref5.props,
        action = _ref5.action;

      if (action.value && !root.ref.hopper) {
        var hopper = createHopper(
          root.element,
          function(items) {
            // these files don't fit so stop here
            if (exceedsMaxFiles(root, items)) {
              return false;
            }

            // all items should be validated by all filters as valid
            var dropValidation = root.query('GET_DROP_VALIDATION');
            return dropValidation
              ? items.every(function(item) {
                  return applyFilters('ALLOW_HOPPER_ITEM', item, {
                    query: root.query
                  }).every(function(result) {
                    return result === true;
                  });
                })
              : true;
          },
          {
            catchesDropsOnPage: root.query('GET_DROP_ON_PAGE'),
            requiresDropOnElement: root.query('GET_DROP_ON_ELEMENT')
          }
        );

        hopper.onload = function(items, position) {
          var itemList = root.ref.list.childViews[0];
          var index = getItemIndexByPosition(itemList, {
            left: position.scopeLeft,
            top:
              position.scopeTop -
              root.ref.list.rect.outer.top +
              root.ref.list.element.scrollTop
          });

          forEachDelayed(items, function(source) {
            root.dispatch('ADD_ITEM', {
              interactionMethod: InteractionMethod.DROP,
              source: source,
              index: index
            });
          });

          root.dispatch('DID_DROP', { position: position });

          root.dispatch('DID_END_DRAG', { position: position });
        };

        hopper.ondragstart = function(position) {
          root.dispatch('DID_START_DRAG', { position: position });
        };

        hopper.ondrag = debounce(function(position) {
          root.dispatch('DID_DRAG', { position: position });
        });

        hopper.ondragend = function(position) {
          root.dispatch('DID_END_DRAG', { position: position });
        };

        root.ref.hopper = hopper;

        root.ref.drip = root.appendChildView(root.createChildView(drip));
      } else if (!action.value && root.ref.hopper) {
        root.ref.hopper.destroy();
        root.ref.hopper = null;
        root.removeChildView(root.ref.drip);
      }
    };

    /**
     * Enable or disable browse functionality
     */
    var toggleAllowBrowse = function toggleAllowBrowse(_ref6) {
      var root = _ref6.root,
        props = _ref6.props,
        action = _ref6.action;

      if (action.value) {
        root.ref.browser = root.appendChildView(
          root.createChildView(
            browser,
            Object.assign({}, props, {
              onload: function onload(items) {
                // these files don't fit so stop here
                if (exceedsMaxFiles(root, items)) {
                  return false;
                }

                // add items!
                forEachDelayed(items, function(source) {
                  root.dispatch('ADD_ITEM', {
                    interactionMethod: InteractionMethod.BROWSE,
                    source: source,
                    index: 0
                  });
                });
              }
            })
          ),
          0
        );
      } else if (root.ref.browser) {
        root.removeChildView(root.ref.browser);
      }
    };

    /**
     * Enable or disable paste functionality
     */
    var toggleAllowPaste = function toggleAllowPaste(_ref7) {
      var root = _ref7.root,
        action = _ref7.action;

      if (action.value) {
        root.ref.paster = createPaster();
        root.ref.paster.onload = function(items) {
          forEachDelayed(items, function(source) {
            root.dispatch('ADD_ITEM', {
              interactionMethod: InteractionMethod.PASTE,
              source: source,
              index: 0
            });
          });
        };
      } else if (root.ref.paster) {
        root.ref.paster.destroy();
        root.ref.paster = null;
      }
    };

    /**
     * Route actions
     */
    var route = createRoute({
      DID_SET_ALLOW_BROWSE: toggleAllowBrowse,
      DID_SET_ALLOW_DROP: toggleAllowDrop,
      DID_SET_ALLOW_PASTE: toggleAllowPaste
    });

    var root = createView({
      name: 'root',
      read: function read(_ref8) {
        var root = _ref8.root;

        if (root.ref.measure) {
          root.ref.measureHeight = root.ref.measure.offsetHeight;
        }
      },
      create: create$1,
      write: write,
      destroy: function destroy(_ref9) {
        var root = _ref9.root;

        if (root.ref.paster) {
          root.ref.paster.destroy();
        }
        if (root.ref.hopper) {
          root.ref.hopper.destroy();
        }
      },
      mixins: {
        styles: ['height']
      }
    });

    // defaults
    // view
    // creates the app
    var createApp$1 = function createApp() {
      var initialOptions =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      // let element
      var originalElement = null;

      // get default options
      var defaultOptions$$1 = getOptions$1();

      // create the data store, this will contain all our app info
      var store = createStore(
        // initial state (should be serializable)
        createInitialState(defaultOptions$$1),

        // queries
        [queries, createOptionQueries(defaultOptions$$1)],

        // action handlers
        [actions, createOptionActions(defaultOptions$$1)]
      );

      // set initial options
      store.dispatch('SET_OPTIONS', { options: initialOptions });

      // render initial view
      var view = root(store, { id: getUniqueId() });

      //
      // PRIVATE API -------------------------------------------------------------------------------------
      //
      var resting = false;
      var hidden = false;

      var readWriteApi = {
        // necessary for update loop

        /**
         * Reads from dom (never call manually)
         * @private
         */
        _read: function _read() {
          // if resting, no need to read as numbers will still all be correct
          if (resting) {
            return;
          }

          // read view data
          view._read();

          // if root is hidden
          hidden = view.rect.element.hidden;
        },

        /**
         * Writes to dom (never call manually)
         * @private
         */
        _write: function _write(ts) {
          // don't do anything while hidden
          if (hidden) {
            return;
          }

          // get all actions from store
          var actions$$1 = store
            .processActionQueue()

            // filter out set actions (these will automatically trigger DID_SET)
            .filter(function(action) {
              return !/^SET_/.test(action.type);
            });

          // if was idling and no actions stop here
          if (resting && !actions$$1.length) {
            return;
          }

          // some actions might trigger events
          routeActionsToEvents(actions$$1);

          // update the view
          resting = view._write(ts, actions$$1);

          // will clean up all archived items
          removeReleasedItems(store.query('GET_ITEMS'));

          // now idling
          if (resting) {
            store.processDispatchQueue();
          }
        }
      };

      //
      // EXPOSE EVENTS -------------------------------------------------------------------------------------
      //
      var createEvent = function createEvent(name) {
        return function(data) {
          // create default event
          var event = {
            type: name
          };

          // no data to add
          if (!data) {
            return event;
          }

          // copy relevant props
          if (data.hasOwnProperty('error')) {
            event.error = data.error ? Object.assign({}, data.error) : null;
          }

          if (data.status) {
            event.status = Object.assign({}, data.status);
          }

          if (data.file) {
            event.output = data.file;
          }

          // only source is available, else add item if possible
          if (data.source) {
            event.file = data.source;
          } else if (data.item || data.id) {
            var item = data.item ? data.item : store.query('GET_ITEM', data.id);
            event.file = item ? createItemAPI(item) : null;
          }

          // map all items in a possible items array
          if (data.items) {
            event.items = data.items.map(createItemAPI);
          }

          // if this is a progress event add the progress amount
          if (/progress/.test(name)) {
            event.progress = data.progress;
          }

          return event;
        };
      };

      var eventRoutes = {
        DID_DESTROY: createEvent('destroy'),

        DID_INIT: createEvent('init'),

        DID_THROW_MAX_FILES: createEvent('warning'),

        DID_START_ITEM_LOAD: createEvent('addfilestart'),
        DID_UPDATE_ITEM_LOAD_PROGRESS: createEvent('addfileprogress'),
        DID_LOAD_ITEM: createEvent('addfile'),

        DID_THROW_ITEM_INVALID: [createEvent('error'), createEvent('addfile')],

        DID_THROW_ITEM_LOAD_ERROR: [createEvent('error'), createEvent('addfile')],

        DID_PREPARE_OUTPUT: createEvent('preparefile'),

        DID_START_ITEM_PROCESSING: createEvent('processfilestart'),
        DID_UPDATE_ITEM_PROCESS_PROGRESS: createEvent('processfileprogress'),
        DID_ABORT_ITEM_PROCESSING: createEvent('processfileabort'),
        DID_COMPLETE_ITEM_PROCESSING: createEvent('processfile'),
        DID_REVERT_ITEM_PROCESSING: createEvent('processfilerevert'),

        DID_THROW_ITEM_PROCESSING_ERROR: [
          createEvent('error'),
          createEvent('processfile')
        ],

        DID_REMOVE_ITEM: createEvent('removefile'),

        DID_UPDATE_ITEMS: createEvent('updatefiles')
      };

      var exposeEvent = function exposeEvent(event) {
        // create event object to be dispatched
        var detail = Object.assign({ pond: exports }, event);
        delete detail.type;
        view.element.dispatchEvent(
          new CustomEvent('FilePond:' + event.type, {
            // event info
            detail: detail,

            // event behaviour
            bubbles: true,
            cancelable: true,
            composed: true // triggers listeners outside of shadow root
          })
        );

        // event object to params used for `on()` event handlers and callbacks `oninit()`
        var params = [];

        // if is possible error event, make it the first param
        if (event.hasOwnProperty('error')) {
          params.push(event.error);
        }
        // file is always section
        if (event.hasOwnProperty('file')) {
          params.push(event.file);
        }

        // append other props
        var filtered = ['type', 'error', 'file'];
        Object.keys(event)
          .filter(function(key) {
            return !filtered.includes(key);
          })
          .forEach(function(key) {
            return params.push(event[key]);
          });

        // on(type, () => { })
        exports.fire.apply(exports, [event.type].concat(params));

        // oninit = () => {}
        var handler = store.query('GET_ON' + event.type.toUpperCase());
        if (handler) {
          handler.apply(undefined, params);
        }
      };

      var routeActionsToEvents = function routeActionsToEvents(actions$$1) {
        if (!actions$$1.length) {
          return;
        }

        actions$$1.forEach(function(action) {
          if (!eventRoutes[action.type]) {
            return;
          }
          var routes = eventRoutes[action.type];
          (Array.isArray(routes) ? routes : [routes]).forEach(function(route) {
            setTimeout(function() {
              exposeEvent(route(action.data));
            }, 0);
          });
        });
      };

      //
      // PUBLIC API -------------------------------------------------------------------------------------
      //
      var setOptions = function setOptions(options) {
        return store.dispatch('SET_OPTIONS', { options: options });
      };

      var getFile = function getFile(query) {
        return store.query('GET_ACTIVE_ITEM', query);
      };

      var addFile = function addFile(source) {
        var options =
          arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        return new Promise(function(resolve, reject) {
          store.dispatch('ADD_ITEM', {
            interactionMethod: InteractionMethod.API,
            source: source,
            index: options.index,
            success: resolve,
            failure: reject,
            options: options
          });
        });
      };

      var removeFile = function removeFile(query) {
        // request item removal
        store.dispatch('REMOVE_ITEM', { query: query });

        // see if item has been removed
        return store.query('GET_ACTIVE_ITEM', query) === null;
      };

      var addFiles = function addFiles() {
        for (
          var _len = arguments.length, args = Array(_len), _key = 0;
          _key < _len;
          _key++
        ) {
          args[_key] = arguments[_key];
        }

        return new Promise(function(resolve, reject) {
          var sources = [];
          var options = {};

          // user passed a sources array
          if (isArray(args[0])) {
            sources.push.apply(sources, toConsumableArray(args[0]));
            Object.assign(options, args[1] || {});
          } else {
            // user passed sources as arguments, last one might be options object
            var lastArgument = args[args.length - 1];
            if (
              (typeof lastArgument === 'undefined'
                ? 'undefined'
                : _typeof(lastArgument)) === 'object' &&
              !(lastArgument instanceof Blob)
            ) {
              Object.assign(options, args.pop());
            }

            // add rest to sources
            sources.push.apply(sources, args);
          }

          var sourcePromises = [];
          var delayPromises = forEachDelayed(sources, function(source) {
            sourcePromises.push(addFile(source, options));
          });

          Promise.all(delayPromises).then(function() {
            Promise.all(sourcePromises).then(function(results) {
              resolve(results);
            });
          });
        });
      };

      var getFiles = function getFiles() {
        return store.query('GET_ACTIVE_ITEMS');
      };

      var processFile = function processFile(query) {
        return new Promise(function(resolve, reject) {
          store.dispatch('REQUEST_ITEM_PROCESSING', {
            query: query,
            success: function success(item) {
              resolve(item);
            },
            failure: function failure(error) {
              reject(error);
            }
          });
        });
      };

      var processFiles = function processFiles() {
        for (
          var _len2 = arguments.length, args = Array(_len2), _key2 = 0;
          _key2 < _len2;
          _key2++
        ) {
          args[_key2] = arguments[_key2];
        }

        var queries$$1 = Array.isArray(args[0]) ? args[0] : args;
        if (!queries$$1.length) {
          var files = getFiles().filter(function(item) {
            return (
              item.status !== ItemStatus.PROCESSING &&
              item.status !== ItemStatus.PROCESSING_COMPLETE
            );
          });
          return Promise.all(files.map(processFile));
        }
        return Promise.all(queries$$1.map(processFile));
      };

      var removeFiles = function removeFiles() {
        for (
          var _len3 = arguments.length, args = Array(_len3), _key3 = 0;
          _key3 < _len3;
          _key3++
        ) {
          args[_key3] = arguments[_key3];
        }

        var queries$$1 = Array.isArray(args[0]) ? args[0] : args;
        var files = getFiles();

        if (!queries$$1.length) {
          return Promise.all(files.map(removeFile));
        }

        // when removing by index the indexes shift after each file removal so we need to convert indexes to ids
        var mappedQueries = queries$$1
          .map(function(query) {
            return isNumber(query)
              ? files[query] ? files[query].id : null
              : query;
          })
          .filter(function(query) {
            return query;
          });

        return mappedQueries.map(removeFile);
      };

      var exports = Object.assign(
        {},
        on(),
        readWriteApi,
        createOptionAPI(store, defaultOptions$$1),
        {
          /**
           * Override options defined in options object
           * @param options
           */
          setOptions: setOptions,

          /**
           * Load the given file
           * @param source - the source of the file (either a File, base64 data uri or url)
           * @param options - object, { index: 0 }
           */
          addFile: addFile,

          /**
           * Load the given files
           * @param sources - the sources of the files to load
           * @param options - object, { index: 0 }
           */
          addFiles: addFiles,

          /**
           * Returns the file objects matching the given query
           * @param query { string, number, null }
           */
          getFile: getFile,

          /**
           * Upload file with given name
           * @param query { string, number, null  }
           */
          processFile: processFile,

          /**
           * Removes a file by its name
           * @param query { string, number, null  }
           */
          removeFile: removeFile,

          /**
           * Returns all files (wrapped in public api)
           */
          getFiles: getFiles,

          /**
           * Starts uploading all files
           */
          processFiles: processFiles,

          /**
           * Clears all files from the files list
           */
          removeFiles: removeFiles,

          /**
           * Browse the file system for a file
           */
          browse: function browse() {
            // needs to be trigger directly as user action needs to be traceable (is not traceable in requestAnimationFrame)
            var input = view.element.querySelector('input[type=file]');
            if (input) {
              input.click();
            }
          },

          /**
           * Destroys the app
           */
          destroy: function destroy() {
            // request destruction
            exports.fire('destroy', view.element);

            // stop active processes (file uploads, fetches, stuff like that)
            // loop over items and depending on states call abort for ongoing processes
            store.dispatch('ABORT_ALL');

            // destroy view
            view._destroy();

            // dispatch destroy
            store.dispatch('DID_DESTROY');
          },

          /**
           * Inserts the plugin before the target element
           */
          insertBefore: function insertBefore$$1(element) {
            return insertBefore(view.element, element);
          },

          /**
           * Inserts the plugin after the target element
           */
          insertAfter: function insertAfter$$1(element) {
            return insertAfter(view.element, element);
          },

          /**
           * Appends the plugin to the target element
           */
          appendTo: function appendTo(element) {
            return element.appendChild(view.element);
          },

          /**
           * Replaces an element with the app
           */
          replaceElement: function replaceElement(element) {
            // insert the app before the element
            insertBefore(view.element, element);

            // remove the original element
            element.parentNode.removeChild(element);

            // remember original element
            originalElement = element;
          },

          /**
           * Restores the original element
           */
          restoreElement: function restoreElement() {
            if (!originalElement) {
              return; // no element to restore
            }

            // restore original element
            insertAfter(originalElement, view.element);

            // remove our element
            view.element.parentNode.removeChild(view.element);

            // remove reference
            originalElement = null;
          },

          /**
           * Returns true if the app root is attached to given element
           * @param element
           */
          isAttachedTo: function isAttachedTo(element) {
            return view.element === element || originalElement === element;
          },

          /**
           * Returns the root element
           */
          element: {
            get: function get$$1() {
              return view.element;
            }
          }
        }
      );

      // Done!
      store.dispatch('DID_INIT');

      // create actual api object
      return createObject(exports);
    };

    var createAppObject = function createAppObject() {
      var customOptions =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      // default options
      var defaultOptions$$1 = {};
      forin(getOptions$1(), function(key, value) {
        defaultOptions$$1[key] = value[0];
      });

      // set app options
      var app = createApp$1(Object.assign({}, defaultOptions$$1, customOptions));

      // return the plugin instance
      return app;
    };

    var lowerCaseFirstLetter = function lowerCaseFirstLetter(string) {
      return string.charAt(0).toLowerCase() + string.slice(1);
    };

    var attributeNameToPropertyName = function attributeNameToPropertyName(
      attributeName
    ) {
      return toCamels(attributeName.replace(/^data-/, ''));
    };

    var mapObject = function mapObject(object, propertyMap) {
      // remove unwanted
      forin(propertyMap, function(selector, mapping) {
        forin(object, function(property, value) {
          // create regexp shortcut
          var selectorRegExp = new RegExp(selector);

          // tests if
          var matches = selectorRegExp.test(property);

          // no match, skip
          if (!matches) {
            return;
          }

          // if there's a mapping, the original property is always removed
          delete object[property];

          // should only remove, we done!
          if (mapping === false) {
            return;
          }

          // move value to new property
          if (isString(mapping)) {
            object[mapping] = value;
            return;
          }

          // move to group
          var group = mapping.group;
          if (isObject(mapping) && !object[group]) {
            object[group] = {};
          }

          object[group][
            lowerCaseFirstLetter(property.replace(selectorRegExp, ''))
          ] = value;
        });

        // do submapping
        if (mapping.mapping) {
          mapObject(object[mapping.group], mapping.mapping);
        }
      });
    };

    var getAttributesAsObject = function getAttributesAsObject(node) {
      var attributeMapping =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      // turn attributes into object
      var attributes = [];
      forin(node.attributes, function(index) {
        attributes.push(node.attributes[index]);
      });

      var output = attributes
        .filter(function(attribute) {
          return attribute.name;
        })
        .reduce(function(obj, attribute) {
          var value = attr(node, attribute.name);

          obj[attributeNameToPropertyName(attribute.name)] =
            value === attribute.name ? true : value;
          return obj;
        }, {});

      // do mapping of object properties
      mapObject(output, attributeMapping);

      return output;
    };

    var createAppAtElement = function createAppAtElement(element) {
      var options =
        arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      // how attributes of the input element are mapped to the options for the plugin
      var attributeMapping = {
        // translate to other name
        '^class$': 'className',
        '^multiple$': 'allowMultiple',
        '^capture$': 'captureMethod',

        // group under single property
        '^server': {
          group: 'server',
          mapping: {
            '^process': {
              group: 'process'
            },
            '^revert': {
              group: 'revert'
            },
            '^fetch': {
              group: 'fetch'
            },
            '^restore': {
              group: 'restore'
            },
            '^load': {
              group: 'load'
            }
          }
        },

        // don't include in object
        '^type$': false,
        '^files$': false
      };

      // add additional option translators
      applyFilters('SET_ATTRIBUTE_TO_OPTION_MAP', attributeMapping);

      // create final options object by setting options object and then overriding options supplied on element
      var mergedOptions = Object.assign({}, options);

      var attributeOptions = getAttributesAsObject(
        element.nodeName === 'FIELDSET'
          ? element.querySelector('input[type=file]')
          : element,
        attributeMapping
      );

      // merge with options object
      Object.keys(attributeOptions).forEach(function(key) {
        if (isObject(attributeOptions[key])) {
          if (!isObject(mergedOptions[key])) {
            mergedOptions[key] = {};
          }
          Object.assign(mergedOptions[key], attributeOptions[key]);
        } else {
          mergedOptions[key] = attributeOptions[key];
        }
      });

      // if parent is a fieldset, get files from parent by selecting all input fields that are not file upload fields
      // these will then be automatically set to the initial files
      mergedOptions.files = (options.files || []).concat(
        []
          .concat(
            toConsumableArray(element.querySelectorAll('input:not([type=file])'))
          )
          .map(function(input) {
            return {
              source: input.value,
              options: {
                type: input.dataset.type
              }
            };
          })
      );

      // build plugin
      var app = createAppObject(mergedOptions);

      // add already selected files
      if (element.files) {
        [].concat(toConsumableArray(element.files)).forEach(function(file) {
          app.addFile(file);
        });
      }

      // replace the target element
      app.replaceElement(element);

      // expose
      return app;
    };

    // if an element is passed, we create the instance at that element, if not, we just create an up object
    var createApp = function createApp() {
      return isNode(arguments.length <= 0 ? undefined : arguments[0])
        ? createAppAtElement.apply(undefined, arguments)
        : createAppObject.apply(undefined, arguments);
    };

    var PRIVATE_METHODS = ['fire', '_read', '_write'];

    var createAppAPI = function createAppAPI(app) {
      var api = {};

      copyObjectPropertiesToObject(app, api, PRIVATE_METHODS);

      return api;
    };

    /**
     * Replaces placeholders in given string with replacements
     * @param string - "Foo {bar}""
     * @param replacements - { "bar": 10 }
     */
    var replaceInString = function replaceInString(string, replacements) {
      return string.replace(/(?:{([a-zA-Z]+)})/g, function(match, group) {
        return replacements[group];
      });
    };

    var images = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tiff'];
    var text$1 = ['css', 'csv', 'html', 'txt'];
    var map = {
      zip: 'zip|compressed',
      epub: 'application/epub+zip'
    };

    var guesstimateMimeType = function guesstimateMimeType() {
      var extension =
        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';

      extension = extension.toLowerCase();
      if (images.includes(extension)) {
        return (
          'image/' +
          (extension === 'jpg'
            ? 'jpeg'
            : extension === 'svg' ? 'svg+xml' : extension)
        );
      }
      if (text$1.includes(extension)) {
        return 'text/' + extension;
      }
      return map[extension] || null;
    };

    var createWorker = function createWorker(fn) {
      var workerBlob = new Blob(['(', fn.toString(), ')()'], {
        type: 'application/javascript'
      });
      var workerURL = URL.createObjectURL(workerBlob);
      var worker = new Worker(workerURL);

      return {
        transfer: function transfer(message, cb) {},
        post: function post(message, cb, transferList) {
          var id = getUniqueId();

          worker.onmessage = function(e) {
            if (e.data.id === id) {
              cb(e.data.message);
            }
          };

          worker.postMessage(
            {
              id: id,
              message: message
            },
            transferList
          );
        },
        terminate: function terminate() {
          worker.terminate();
          URL.revokeObjectURL(workerURL);
        }
      };
    };

    var loadImage = function loadImage(url) {
      return new Promise(function(resolve, reject) {
        var img = new Image();
        img.onload = function() {
          resolve(img);
        };
        img.onerror = function(e) {
          reject(e);
        };
        img.src = url;
      });
    };

    var renameFile = function renameFile(file, name) {
      var renamedFile = file.slice(0, file.size, file.type);
      renamedFile.lastModifiedDate = file.lastModifiedDate;
      renamedFile.name = name;
      return renamedFile;
    };

    var copyFile = function copyFile(file) {
      return renameFile(file, file.name);
    };

    // already registered plugins (can't register twice)
    var registeredPlugins = [];

    // pass utils to plugin
    var createAppPlugin = function createAppPlugin(plugin) {
      // already registered
      if (registeredPlugins.includes(plugin)) {
        return;
      }

      // remember this plugin
      registeredPlugins.push(plugin);

      // setup!
      var pluginOutline = plugin({
        addFilter: addFilter,
        utils: {
          Type: Type,
          forin: forin,
          isString: isString,
          isFile: isFile,
          toNaturalFileSize: toNaturalFileSize,
          replaceInString: replaceInString,
          getExtensionFromFilename: getExtensionFromFilename,
          getFilenameWithoutExtension: getFilenameWithoutExtension,
          guesstimateMimeType: guesstimateMimeType,
          getFileFromBlob: getFileFromBlob,
          getFilenameFromURL: getFilenameFromURL,
          createRoute: createRoute,
          createWorker: createWorker,
          createView: createView,
          createItemAPI: createItemAPI,
          loadImage: loadImage,
          copyFile: copyFile,
          renameFile: renameFile,
          createBlob: createBlob,
          applyFilterChain: applyFilterChain,
          text: text,
          getNumericAspectRatioFromString: getNumericAspectRatioFromString
        },
        views: {
          fileActionButton: fileActionButton
        }
      });

      // add plugin options to default options
      extendDefaultOptions(pluginOutline.options);
    };

    // feature detection used by supported() method
    var isOperaMini = function isOperaMini() {
      return (
        Object.prototype.toString.call(window.operamini) === '[object OperaMini]'
      );
    };
    var hasPromises = function hasPromises() {
      return 'Promise' in window;
    };
    var hasBlobSlice = function hasBlobSlice() {
      return 'slice' in Blob.prototype;
    };
    var hasCreateObjectURL = function hasCreateObjectURL() {
      return 'URL' in window && 'createObjectURL' in window.URL;
    };
    var hasVisibility = function hasVisibility() {
      return 'visibilityState' in document;
    };
    var hasTiming = function hasTiming() {
      return 'performance' in window;
    }; // iOS 8.x
    var isBrowser = function isBrowser() {
      return (
        typeof window !== 'undefined' && typeof window.document !== 'undefined'
      );
    };

    var supported = (function() {
      // Runs immidiately and then remembers result for subsequent calls
      var isSupported =
        // Has to be a browser
        isBrowser() &&
        // Can't run on Opera Mini due to lack of everything
        !isOperaMini() &&
        // Require these APIs to feature detect a modern browser
        hasVisibility() &&
        hasPromises() &&
        hasBlobSlice() &&
        hasCreateObjectURL() &&
        hasTiming();

      return function() {
        return isSupported;
      };
    })();

    /**
     * Plugin internal state (over all instances)
     */
    var state = {
      // active app instances, used to redraw the apps and to find the later
      apps: []
    };

    // plugin name
    var name = 'filepond';

    /**
     * Public Plugin methods
     */
    var fn = function fn() {};
    exports.FileStatus = {};
    exports.OptionTypes = {};
    exports.create = fn;
    exports.destroy = fn;
    exports.parse = fn;
    exports.find = fn;
    exports.registerPlugin = fn;
    exports.getOptions = fn;
    exports.setOptions = fn;
    exports.FileOrigin = {};

    // if not supported, no API
    if (supported()) {
      // start painter and fire load event
      createPainter(
        function() {
          state.apps.forEach(function(app) {
            return app._read();
          });
        },
        function(ts) {
          state.apps.forEach(function(app) {
            return app._write(ts);
          });
        }
      );

      // fire loaded event so we know when FilePond is available
      var dispatch = function dispatch() {
        // let others know we have area ready
        document.dispatchEvent(
          new CustomEvent('FilePond:loaded', {
            detail: {
              supported: supported,
              create: exports.create,
              destroy: exports.destroy,
              parse: exports.parse,
              find: exports.find,
              registerPlugin: exports.registerPlugin,
              setOptions: exports.setOptions
            }
          })
        );

        // clean up event
        document.removeEventListener('DOMContentLoaded', dispatch);
      };

      if (document.readyState !== 'loading') {
        // move to back of execution queue, FilePond should have been exported by then
        setTimeout(function() {
          return dispatch();
        }, 0);
      } else {
        document.addEventListener('DOMContentLoaded', dispatch);
      }

      // updates the OptionTypes object based on the current options
      var updateOptionTypes = function updateOptionTypes() {
        return forin(getOptions$1(), function(key, value) {
          exports.OptionTypes[key] = value[1];
        });
      };

      exports.FileOrigin = Object.assign({}, FileOrigin$1);
      exports.FileStatus = Object.assign({}, ItemStatus);

      exports.OptionTypes = {};
      updateOptionTypes();

      // create method, creates apps and adds them to the app array
      exports.create = function create() {
        var app = createApp.apply(undefined, arguments);
        app.on('destroy', exports.destroy);
        state.apps.push(app);
        return createAppAPI(app);
      };

      // destroys apps and removes them from the app array
      exports.destroy = function destroy(hook) {
        // returns true if the app was destroyed successfully
        var indexToRemove = state.apps.findIndex(function(app) {
          return app.isAttachedTo(hook);
        });
        if (indexToRemove >= 0) {
          // remove from apps
          var app = state.apps.splice(indexToRemove, 1)[0];

          // restore original dom element
          app.restoreElement();

          return true;
        }

        return false;
      };

      // parses the given context for plugins (does not include the context element itself)
      exports.parse = function parse(context) {
        // get all possible hooks
        var matchedHooks = [].concat(
          toConsumableArray(context.querySelectorAll('.' + name))
        );

        // filter out already active hooks
        var newHooks = matchedHooks.filter(function(newHook) {
          return !state.apps.find(function(app) {
            return app.isAttachedTo(newHook);
          });
        });

        // create new instance for each hook
        return newHooks.map(function(hook) {
          return exports.create(hook);
        });
      };

      // returns an app based on the given element hook
      exports.find = function find(hook) {
        var app = state.apps.find(function(app) {
          return app.isAttachedTo(hook);
        });
        if (!app) {
          return null;
        }
        return createAppAPI(app);
      };

      // adds a plugin extension
      exports.registerPlugin = function registerPlugin() {
        for (
          var _len = arguments.length, plugins = Array(_len), _key = 0;
          _key < _len;
          _key++
        ) {
          plugins[_key] = arguments[_key];
        }

        // register plugins
        plugins.forEach(createAppPlugin);

        // update OptionTypes, each plugin might have extended the default options
        updateOptionTypes();
      };

      exports.getOptions = function getOptions$$1() {
        var opts = {};
        forin(getOptions$1(), function(key, value) {
          opts[key] = value[0];
        });
        return opts;
      };

      exports.setOptions = function setOptions$$1(opts) {
        if (isObject(opts)) {
          // update existing plugins
          state.apps.forEach(function(app) {
            app.setOptions(opts);
          });

          // override defaults
          setOptions$1(opts);
        }

        // return new options
        return exports.getOptions();
      };
    }

    exports.supported = supported;

    Object.defineProperty(exports, '__esModule', { value: true });
});
